diff --git a/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h b/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h index 0c6406d861851..241a3fc109360 100644 --- a/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h +++ b/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h @@ -34,6 +34,12 @@ struct DisableLIRP { /// When true, Memcpy is disabled. static bool Memcpy; + + /// When true, Strlen is disabled. + static bool Strlen; + + /// When true, Wcslen is disabled. + static bool Wcslen; }; /// Performs Loop Idiom Recognize Pass. diff --git a/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h b/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h index a8fb38e726004..50f695dbe6c07 100644 --- a/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h +++ b/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h @@ -93,6 +93,12 @@ namespace llvm { Value *emitStrLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL, const TargetLibraryInfo *TLI); + /// Emit a call to the wcslen function to the builder, for the specified + /// pointer. Ptr is required to be some pointer type, and the return value has + /// 'size_t' type. + Value *emitWcsLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL, + const TargetLibraryInfo *TLI); + /// Emit a call to the strdup function to the builder, for the specified /// pointer. Ptr is required to be some pointer type, and the return value has /// 'i8*' type. diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp index 2462ec33e0c20..f90d7967cb577 100644 --- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp +++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp @@ -20,8 +20,7 @@ // // TODO List: // -// Future loop memory idioms to recognize: -// memcmp, strlen, etc. +// Future loop memory idioms to recognize: memcmp, etc. // // This could recognize common matrix multiplies and dot product idioms and // replace them with calls to BLAS (if linked in??). @@ -33,6 +32,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" @@ -97,6 +97,7 @@ using namespace llvm; STATISTIC(NumMemSet, "Number of memset's formed from loop stores"); STATISTIC(NumMemCpy, "Number of memcpy's formed from loop load+stores"); STATISTIC(NumMemMove, "Number of memmove's formed from loop load+stores"); +STATISTIC(NumStrLen, "Number of strlen's and wcslen's formed from loop loads"); STATISTIC( NumShiftUntilBitTest, "Number of uncountable loops recognized as 'shift until bitttest' idiom"); @@ -126,6 +127,22 @@ static cl::opt cl::location(DisableLIRP::Memcpy), cl::init(false), cl::ReallyHidden); +bool DisableLIRP::Strlen; +static cl::opt + DisableLIRPStrlen("disable-loop-idiom-strlen", + cl::desc("Proceed with loop idiom recognize pass, but do " + "not convert loop(s) to strlen."), + cl::location(DisableLIRP::Strlen), cl::init(false), + cl::ReallyHidden); + +bool DisableLIRP::Wcslen; +static cl::opt + EnableLIRPWcslen("disable-loop-idiom-wcslen", + cl::desc("Proceed with loop idiom recognize pass, " + "enable conversion of loop(s) to wcslen."), + cl::location(DisableLIRP::Wcslen), cl::init(false), + cl::ReallyHidden); + static cl::opt UseLIRCodeSizeHeurs( "use-lir-code-size-heurs", cl::desc("Use loop idiom recognition code size heuristics when compiling " @@ -246,6 +263,7 @@ class LoopIdiomRecognize { bool recognizeShiftUntilBitTest(); bool recognizeShiftUntilZero(); + bool recognizeAndInsertStrLen(); /// @} }; @@ -1494,7 +1512,17 @@ bool LoopIdiomRecognize::runOnNoncountableLoop() { return recognizePopcount() || recognizeAndInsertFFS() || recognizeShiftUntilBitTest() || recognizeShiftUntilZero() || - recognizeShiftUntilLessThan(); + recognizeShiftUntilLessThan() || recognizeAndInsertStrLen(); +} + +/// Check if a Value is either a nullptr or a constant int zero +static bool isZeroConstant(const Value *Val) { + if (isa(Val)) + return true; + const ConstantInt *CmpZero = dyn_cast(Val); + if (!CmpZero || !CmpZero->isZero()) + return false; + return true; } /// Check if the given conditional branch is based on the comparison between @@ -1512,8 +1540,7 @@ static Value *matchCondition(BranchInst *BI, BasicBlock *LoopEntry, if (!Cond) return nullptr; - ConstantInt *CmpZero = dyn_cast(Cond->getOperand(1)); - if (!CmpZero || !CmpZero->isZero()) + if (!isZeroConstant(Cond->getOperand(1))) return nullptr; BasicBlock *TrueSucc = BI->getSuccessor(0); @@ -1529,6 +1556,279 @@ static Value *matchCondition(BranchInst *BI, BasicBlock *LoopEntry, return nullptr; } +namespace { + +class StrlenVerifier { +public: + explicit StrlenVerifier(const Loop *CurLoop, ScalarEvolution *SE, + const TargetLibraryInfo *TLI) + : CurLoop(CurLoop), SE(SE), TLI(TLI) {} + + bool isValidStrlenIdiom() { + // Give up if the loop has multiple blocks, multiple backedges, or + // multiple exit blocks + if (CurLoop->getNumBackEdges() != 1 || CurLoop->getNumBlocks() != 1 || + !CurLoop->getUniqueExitBlock()) + return false; + + // It should have a preheader and a branch instruction. + BasicBlock *Preheader = CurLoop->getLoopPreheader(); + if (!Preheader) + return false; + + BranchInst *EntryBI = dyn_cast(Preheader->getTerminator()); + if (!EntryBI) + return false; + + // The loop exit must be conditioned on an icmp with 0 the null terminator. + // The icmp operand has to be a load on some SSA reg that increments + // by 1 in the loop. + BasicBlock *LoopBody = *CurLoop->block_begin(); + + // Skip if the body is too big as it most likely is not a strlen idiom. + if (!LoopBody || LoopBody->size() >= 15) + return false; + + BranchInst *LoopTerm = dyn_cast(LoopBody->getTerminator()); + Value *LoopCond = matchCondition(LoopTerm, LoopBody); + if (!LoopCond) + return false; + + LoadInst *LoopLoad = dyn_cast(LoopCond); + if (!LoopLoad || LoopLoad->getPointerAddressSpace() != 0) + return false; + + OperandType = LoopLoad->getType(); + if (!OperandType || !OperandType->isIntegerTy()) + return false; + + // See if the pointer expression is an AddRec with constant step a of form + // ({n,+,a}) where a is the width of the char type. + Value *IncPtr = LoopLoad->getPointerOperand(); + const SCEVAddRecExpr *LoadEv = + dyn_cast(SE->getSCEV(IncPtr)); + if (!LoadEv || LoadEv->getLoop() != CurLoop || !LoadEv->isAffine()) + return false; + LoadBaseEv = LoadEv->getStart(); + + LLVM_DEBUG({ + dbgs() << "pointer load scev: "; + LoadEv->print(outs()); + dbgs() << "\n"; + }); + + const SCEVConstant *Step = + dyn_cast(LoadEv->getStepRecurrence(*SE)); + if (!Step) + return false; + + unsigned StepSize = 0; + StepSizeCI = dyn_cast(Step->getValue()); + if (!StepSizeCI) + return false; + StepSize = StepSizeCI->getZExtValue(); + + // Verify that StepSize is consistent with platform char width. + OpWidth = OperandType->getIntegerBitWidth(); + unsigned WcharSize = TLI->getWCharSize(*LoopLoad->getModule()); + if (OpWidth != StepSize * 8) + return false; + if (OpWidth != 8 && OpWidth != 16 && OpWidth != 32) + return false; + if (OpWidth >= 16) + if (OpWidth != WcharSize * 8) + return false; + + // Scan every instruction in the loop to ensure there are no side effects. + for (Instruction &I : *LoopBody) + if (I.mayHaveSideEffects()) + return false; + + BasicBlock *LoopExitBB = CurLoop->getExitBlock(); + if (!LoopExitBB) + return false; + + for (PHINode &PN : LoopExitBB->phis()) { + if (!SE->isSCEVable(PN.getType())) + return false; + + const SCEV *Ev = SE->getSCEV(&PN); + if (!Ev) + return false; + + LLVM_DEBUG({ + dbgs() << "loop exit phi scev: "; + Ev->print(dbgs()); + dbgs() << "\n"; + }); + + // Since we verified that the loop trip count will be a valid strlen + // idiom, we can expand all lcssa phi with {n,+,1} as (n + strlen) and use + // SCEVExpander materialize the loop output. + const SCEVAddRecExpr *AddRecEv = dyn_cast(Ev); + if (!AddRecEv || !AddRecEv->isAffine()) + return false; + + // We only want RecAddExpr with recurrence step that is constant. This + // is good enough for all the idioms we want to recognize. Later we expand + // and materialize the recurrence as {base,+,a} -> (base + a * strlen) + if (!dyn_cast(AddRecEv->getStepRecurrence(*SE))) + return false; + } + + return true; + } + +public: + const Loop *CurLoop; + ScalarEvolution *SE; + const TargetLibraryInfo *TLI; + + unsigned OpWidth; + ConstantInt *StepSizeCI; + const SCEV *LoadBaseEv; + Type *OperandType; +}; + +} // namespace + +/// The Strlen Idiom we are trying to detect has the following structure +/// +/// preheader: +/// ... +/// br label %body, ... +/// +/// body: +/// ... ; %0 is incremented by a gep +/// %1 = load i8, ptr %0, align 1 +/// %2 = icmp eq i8 %1, 0 +/// br i1 %2, label %exit, label %body +/// +/// exit: +/// %lcssa = phi [%0, %body], ... +/// +/// We expect the strlen idiom to have a load of a character type that +/// is compared against '\0', and such load pointer operand must have scev +/// expression of the form {%str,+,c} where c is a ConstantInt of the +/// appropiate character width for the idiom, and %str is the base of the string +/// And, that all lcssa phis have the form {...,+,n} where n is a constant, +/// +/// When transforming the output of the strlen idiom, the lccsa phi are +/// expanded using SCEVExpander as {base scev,+,a} -> (base scev + a * strlen) +/// and all subsequent uses are replaced. For example, +/// +/// \code{.c} +/// const char* base = str; +/// while (*str != '\0') +/// ++str; +/// size_t result = str - base; +/// \endcode +/// +/// will be transformed as follows: The idiom will be replaced by a strlen +/// computation to compute the address of the null terminator of the string. +/// +/// \code{.c} +/// const char* base = str; +/// const char* end = base + strlen(str); +/// size_t result = end - base; +/// \endcode +/// +/// In the case we index by an induction variable, as long as the induction +/// variable has a constant int increment, we can replace all such indvars +/// with the closed form computation of strlen +/// +/// \code{.c} +/// size_t i = 0; +/// while (str[i] != '\0') +/// ++i; +/// size_t result = i; +/// \endcode +/// +/// Will be replaced by +/// +/// \code{.c} +/// size_t i = 0 + strlen(str); +/// size_t result = i; +/// \endcode +/// +bool LoopIdiomRecognize::recognizeAndInsertStrLen() { + if (DisableLIRP::All) + return false; + + StrlenVerifier Verifier(CurLoop, SE, TLI); + + if (!Verifier.isValidStrlenIdiom()) + return false; + + BasicBlock *Preheader = CurLoop->getLoopPreheader(); + BasicBlock *LoopExitBB = CurLoop->getExitBlock(); + + IRBuilder<> Builder(Preheader->getTerminator()); + SCEVExpander Expander(*SE, Preheader->getModule()->getDataLayout(), + "strlen_idiom"); + Value *MaterialzedBase = Expander.expandCodeFor( + Verifier.LoadBaseEv, Verifier.LoadBaseEv->getType(), + Builder.GetInsertPoint()); + + Value *StrLenFunc = nullptr; + if (Verifier.OpWidth == 8) { + if (DisableLIRP::Strlen) + return false; + if (!isLibFuncEmittable(Preheader->getModule(), TLI, LibFunc_strlen)) + return false; + StrLenFunc = emitStrLen(MaterialzedBase, Builder, *DL, TLI); + } else { + if (DisableLIRP::Wcslen) + return false; + if (!isLibFuncEmittable(Preheader->getModule(), TLI, LibFunc_wcslen)) + return false; + StrLenFunc = emitWcsLen(MaterialzedBase, Builder, *DL, TLI); + } + assert(StrLenFunc && "Failed to emit strlen function."); + + const SCEV *StrlenEv = SE->getSCEV(StrLenFunc); + SmallVector Cleanup; + for (PHINode &PN : LoopExitBB->phis()) { + // We can now materialize the loop output as all phi have scev {base,+,a}. + // We expand the phi as: + // %strlen = call i64 @strlen(%str) + // %phi.new = base expression + step * %strlen + const SCEV *Ev = SE->getSCEV(&PN); + const SCEVAddRecExpr *AddRecEv = dyn_cast(Ev); + const SCEVConstant *Step = + dyn_cast(AddRecEv->getStepRecurrence(*SE)); + const SCEV *Base = AddRecEv->getStart(); + + // It is safe to truncate to base since if base is narrower than size_t + // the equivalent user code will have to truncate anyways. + const SCEV *NewEv = SE->getAddExpr( + Base, SE->getMulExpr(Step, SE->getTruncateOrSignExtend( + StrlenEv, Base->getType()))); + + Value *MaterializedPHI = Expander.expandCodeFor(NewEv, NewEv->getType(), + Builder.GetInsertPoint()); + Expander.clear(); + PN.replaceAllUsesWith(MaterializedPHI); + Cleanup.push_back(&PN); + } + + // All LCSSA Loop Phi are dead, the left over dead loop body can be cleaned + // up by later passes + for (PHINode *PN : Cleanup) + RecursivelyDeleteDeadPHINode(PN); + SE->forgetLoop(CurLoop); + + ++NumStrLen; + LLVM_DEBUG(dbgs() << " Formed strlen idiom: " << *StrLenFunc << "\n"); + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "recognizeAndInsertStrLen", + CurLoop->getStartLoc(), Preheader) + << "Transformed " << StrLenFunc->getName() << " loop idiom"; + }); + + return true; +} + /// Check if the given conditional branch is based on an unsigned less-than /// comparison between a variable and a constant, and if the comparison is false /// the control yields to the loop entry. If the branch matches the behaviour, diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp index 2301be6977cef..24eefc91117b4 100644 --- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp @@ -1582,6 +1582,15 @@ Value *llvm::emitStrLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL, return emitLibCall(LibFunc_strlen, SizeTTy, CharPtrTy, Ptr, B, TLI); } +Value *llvm::emitWcsLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL, + const TargetLibraryInfo *TLI) { + assert(Ptr && Ptr->getType()->isPointerTy() && + "Argument to wcslen intrinsic must be a pointer."); + Type *PtrTy = B.getPtrTy(); + Type *SizeTTy = getSizeTTy(B, TLI); + return emitLibCall(LibFunc_wcslen, SizeTTy, PtrTy, Ptr, B, TLI); +} + Value *llvm::emitStrDup(Value *Ptr, IRBuilderBase &B, const TargetLibraryInfo *TLI) { Type *CharPtrTy = B.getPtrTy(); diff --git a/llvm/test/Transforms/LoopIdiom/strlen-not-emittable.ll b/llvm/test/Transforms/LoopIdiom/strlen-not-emittable.ll new file mode 100644 index 0000000000000..0be76cbd42a72 --- /dev/null +++ b/llvm/test/Transforms/LoopIdiom/strlen-not-emittable.ll @@ -0,0 +1,66 @@ +; RUN: opt -passes='loop(loop-idiom),verify' < %s -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; invalid libcall prototype +declare void @strlen(i32) +declare void @wcslen(i32) + +define i64 @valid_wcslen32(ptr %src) { +; CHECK-LABEL: valid_wcslen32 +; CHECK-NOT: call {{.*}} @wcslen +entry: + %cmp = icmp eq ptr %src, null + br i1 %cmp, label %return, label %lor.lhs.false + +lor.lhs.false: ; preds = %entry + %0 = load i32, ptr %src, align 4 + %cmp1 = icmp eq i32 %0, 0 + br i1 %cmp1, label %return, label %while.cond.preheader + +while.cond.preheader: ; preds = %lor.lhs.false + br label %while.cond + +while.cond: ; preds = %while.cond.preheader, %while.cond + %src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %while.cond.preheader ] + %curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 4 + %1 = load i32, ptr %curr.0, align 4 + %tobool.not = icmp eq i32 %1, 0 + br i1 %tobool.not, label %while.end, label %while.cond + +while.end: ; preds = %while.cond + %curr.0.lcssa = phi ptr [ %curr.0, %while.cond ] + %sub.ptr.lhs.cast = ptrtoint ptr %curr.0.lcssa to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %src to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + %sub.ptr.div = ashr exact i64 %sub.ptr.sub, 2 + br label %return + +return: ; preds = %entry, %lor.lhs.false, %while.end + %retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ] + ret i64 %retval.0 +} + +define i64 @valid_strlen(ptr %str) { +; CHECK-LABEL: valid_strlen +; CHECK-NOT: call {{.*}} @strlen +entry: + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.cond ] + %0 = load i8, ptr %str.addr.0, align 1 + %cmp.not = icmp eq i8 %0, 0 + %incdec.ptr = getelementptr i8, ptr %str.addr.0, i64 1 + br i1 %cmp.not, label %while.end, label %while.cond + +while.end: + %sub.ptr.lhs.cast = ptrtoint ptr %str.addr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + ret i64 %sub.ptr.sub +} + +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"wchar_size", i32 4} diff --git a/llvm/test/Transforms/LoopIdiom/strlen.ll b/llvm/test/Transforms/LoopIdiom/strlen.ll new file mode 100644 index 0000000000000..137a17f541cd4 --- /dev/null +++ b/llvm/test/Transforms/LoopIdiom/strlen.ll @@ -0,0 +1,614 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes='loop(loop-idiom),verify' < %s -S | FileCheck %s + +declare void @other() +declare void @use(ptr) +declare void @usei(i32) +declare void @usel(i64) + +; size_t basic_strlen(const char* str) { +; while (*str != '\0') { +; ++str; +; } +; return str - base; +; } +define i64 @valid_basic_strlen(ptr %str) { +; CHECK-LABEL: define i64 @valid_basic_strlen( +; CHECK-SAME: ptr [[STR:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[STR]]) +; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[STR]], i64 [[STRLEN]] +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[STR_ADDR_0]], align 1 +; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 1 +; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[SCEVGEP]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: ret i64 [[SUB_PTR_SUB]] +; +entry: + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.cond ] + %0 = load i8, ptr %str.addr.0, align 1 + %cmp.not = icmp eq i8 %0, 0 + %incdec.ptr = getelementptr i8, ptr %str.addr.0, i64 1 + br i1 %cmp.not, label %while.end, label %while.cond + +while.end: + %sub.ptr.lhs.cast = ptrtoint ptr %str.addr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + ret i64 %sub.ptr.sub +} + +; int valid_basic_strlen_rotated(const char* str) { +; const char* base = str; +; if (!*str) return 0; +; do { +; ++str; +; } while (*str); +; return str - base; +; } +define i32 @valid_basic_strlen_rotated(ptr %str) { +; CHECK-LABEL: define i32 @valid_basic_strlen_rotated( +; CHECK-SAME: ptr [[STR:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[STR]], align 1 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CLEANUP:.*]], label %[[DO_BODY_PREHEADER:.*]] +; CHECK: [[DO_BODY_PREHEADER]]: +; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[STR]], i64 1 +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[SCEVGEP]]) +; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[STRLEN]], 1 +; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[STR]], i64 [[TMP1]] +; CHECK-NEXT: br label %[[DO_BODY:.*]] +; CHECK: [[DO_BODY]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[INCDEC_PTR:%.*]], %[[DO_BODY]] ], [ [[STR]], %[[DO_BODY_PREHEADER]] ] +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[STR_ADDR_0]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[INCDEC_PTR]], align 1 +; CHECK-NEXT: [[TOBOOL1_NOT:%.*]] = icmp eq i8 [[TMP2]], 0 +; CHECK-NEXT: br i1 [[TOBOOL1_NOT]], label %[[DO_END:.*]], label %[[DO_BODY]] +; CHECK: [[DO_END]]: +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[SCEVGEP1]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[SUB_PTR_SUB]] to i32 +; CHECK-NEXT: br label %[[CLEANUP]] +; CHECK: [[CLEANUP]]: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[CONV]], %[[DO_END]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i32 [[RETVAL_0]] +; +entry: + %0 = load i8, ptr %str, align 1 + %tobool.not = icmp eq i8 %0, 0 + br i1 %tobool.not, label %cleanup, label %do.body + +do.body: + %str.addr.0 = phi ptr [ %incdec.ptr, %do.body ], [ %str, %entry ] + %incdec.ptr = getelementptr inbounds nuw i8, ptr %str.addr.0, i64 1 + %1 = load i8, ptr %incdec.ptr, align 1 + %tobool1.not = icmp eq i8 %1, 0 + br i1 %tobool1.not, label %do.end, label %do.body + +do.end: + %sub.ptr.lhs.cast = ptrtoint ptr %incdec.ptr to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + %conv = trunc i64 %sub.ptr.sub to i32 + br label %cleanup + +cleanup: + %retval.0 = phi i32 [ %conv, %do.end ], [ 0, %entry ] + ret i32 %retval.0 +} + +; int valid_strlen_with_aux_indvar(const char* str) { +; int count = 0; +; int count_offset = -10; +; int count_multiple = 0; +; +; while (*str) { +; ++str; +; ++count; +; ++count_offset; +; count_multiple += 2; +; ++foo; +; } +; +; usei(count); +; usei(count_offset); +; usei(count_multiple); +; use(str); +; use(foo); +; } +define dso_local void @valid_strlen_with_aux_indvar(ptr noundef %str, ptr noundef %foo) local_unnamed_addr { +; CHECK-LABEL: define dso_local void @valid_strlen_with_aux_indvar( +; CHECK-SAME: ptr noundef [[STR:%.*]], ptr noundef [[FOO:%.*]]) local_unnamed_addr { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[STR]], align 1 +; CHECK-NEXT: [[TOBOOL_NOT9:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT9]], label %[[WHILE_END:.*]], label %[[WHILE_BODY_PREHEADER:.*]] +; CHECK: [[WHILE_BODY_PREHEADER]]: +; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[STR]], i64 1 +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[SCEVGEP]]) +; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[STRLEN]], 1 +; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[STR]], i64 [[TMP1]] +; CHECK-NEXT: [[TMP2:%.*]] = trunc i64 [[STRLEN]] to i32 +; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], 1 +; CHECK-NEXT: [[TMP4:%.*]] = trunc i64 [[STRLEN]] to i32 +; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], -9 +; CHECK-NEXT: [[TMP6:%.*]] = trunc i64 [[STRLEN]] to i32 +; CHECK-NEXT: [[TMP7:%.*]] = shl i32 [[TMP6]], 1 +; CHECK-NEXT: [[TMP8:%.*]] = add i32 [[TMP7]], 2 +; CHECK-NEXT: [[TMP9:%.*]] = add i64 [[STRLEN]], 1 +; CHECK-NEXT: [[SCEVGEP2:%.*]] = getelementptr i8, ptr [[FOO]], i64 [[TMP9]] +; CHECK-NEXT: br label %[[WHILE_BODY:.*]] +; CHECK: [[WHILE_BODY]]: +; CHECK-NEXT: [[COUNT_MULTIPLE_014:%.*]] = phi i32 [ [[ADD:%.*]], %[[WHILE_BODY]] ], [ 0, %[[WHILE_BODY_PREHEADER]] ] +; CHECK-NEXT: [[COUNT_OFFSET_013:%.*]] = phi i32 [ [[INC1:%.*]], %[[WHILE_BODY]] ], [ -10, %[[WHILE_BODY_PREHEADER]] ] +; CHECK-NEXT: [[COUNT_012:%.*]] = phi i32 [ [[INC:%.*]], %[[WHILE_BODY]] ], [ 0, %[[WHILE_BODY_PREHEADER]] ] +; CHECK-NEXT: [[FOO_ADDR_011:%.*]] = phi ptr [ [[INCDEC_PTR2:%.*]], %[[WHILE_BODY]] ], [ [[FOO]], %[[WHILE_BODY_PREHEADER]] ] +; CHECK-NEXT: [[STR_ADDR_010:%.*]] = phi ptr [ [[INCDEC_PTR:%.*]], %[[WHILE_BODY]] ], [ [[STR]], %[[WHILE_BODY_PREHEADER]] ] +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[STR_ADDR_010]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[COUNT_012]], 1 +; CHECK-NEXT: [[INC1]] = add nsw i32 [[COUNT_OFFSET_013]], 1 +; CHECK-NEXT: [[ADD]] = add nuw nsw i32 [[COUNT_MULTIPLE_014]], 2 +; CHECK-NEXT: [[INCDEC_PTR2]] = getelementptr inbounds nuw i8, ptr [[FOO_ADDR_011]], i64 1 +; CHECK-NEXT: [[TMP10:%.*]] = load i8, ptr [[INCDEC_PTR]], align 1 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i8 [[TMP10]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END_LOOPEXIT:.*]], label %[[WHILE_BODY]] +; CHECK: [[WHILE_END_LOOPEXIT]]: +; CHECK-NEXT: br label %[[WHILE_END]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[STR_ADDR_0_LCSSA:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[SCEVGEP1]], %[[WHILE_END_LOOPEXIT]] ] +; CHECK-NEXT: [[FOO_ADDR_0_LCSSA:%.*]] = phi ptr [ [[FOO]], %[[ENTRY]] ], [ [[SCEVGEP2]], %[[WHILE_END_LOOPEXIT]] ] +; CHECK-NEXT: [[COUNT_0_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[TMP3]], %[[WHILE_END_LOOPEXIT]] ] +; CHECK-NEXT: [[COUNT_OFFSET_0_LCSSA:%.*]] = phi i32 [ -10, %[[ENTRY]] ], [ [[TMP5]], %[[WHILE_END_LOOPEXIT]] ] +; CHECK-NEXT: [[COUNT_MULTIPLE_0_LCSSA:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[TMP8]], %[[WHILE_END_LOOPEXIT]] ] +; CHECK-NEXT: tail call void @usei(i32 noundef [[COUNT_0_LCSSA]]) +; CHECK-NEXT: tail call void @usei(i32 noundef [[COUNT_OFFSET_0_LCSSA]]) +; CHECK-NEXT: tail call void @usei(i32 noundef [[COUNT_MULTIPLE_0_LCSSA]]) +; CHECK-NEXT: tail call void @use(ptr noundef nonnull [[STR_ADDR_0_LCSSA]]) +; CHECK-NEXT: tail call void @use(ptr noundef [[FOO_ADDR_0_LCSSA]]) +; CHECK-NEXT: ret void +; +entry: + %0 = load i8, ptr %str, align 1 + %tobool.not9 = icmp eq i8 %0, 0 + br i1 %tobool.not9, label %while.end, label %while.body + +while.body: + %count_multiple.014 = phi i32 [ %add, %while.body ], [ 0, %entry ] + %count_offset.013 = phi i32 [ %inc1, %while.body ], [ -10, %entry ] + %count.012 = phi i32 [ %inc, %while.body ], [ 0, %entry ] + %foo.addr.011 = phi ptr [ %incdec.ptr2, %while.body ], [ %foo, %entry ] + %str.addr.010 = phi ptr [ %incdec.ptr, %while.body ], [ %str, %entry ] + %incdec.ptr = getelementptr inbounds nuw i8, ptr %str.addr.010, i64 1 + %inc = add nuw nsw i32 %count.012, 1 + %inc1 = add nsw i32 %count_offset.013, 1 + %add = add nuw nsw i32 %count_multiple.014, 2 + %incdec.ptr2 = getelementptr inbounds nuw i8, ptr %foo.addr.011, i64 1 + %1 = load i8, ptr %incdec.ptr, align 1 + %tobool.not = icmp eq i8 %1, 0 + br i1 %tobool.not, label %while.end, label %while.body + +while.end: + %str.addr.0.lcssa = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.body ] + %foo.addr.0.lcssa = phi ptr [ %foo, %entry ], [ %incdec.ptr2, %while.body ] + %count.0.lcssa = phi i32 [ 0, %entry ], [ %inc, %while.body ] + %count_offset.0.lcssa = phi i32 [ -10, %entry ], [ %inc1, %while.body ] + %count_multiple.0.lcssa = phi i32 [ 0, %entry ], [ %add, %while.body ] + tail call void @usei(i32 noundef %count.0.lcssa) #3 + tail call void @usei(i32 noundef %count_offset.0.lcssa) #3 + tail call void @usei(i32 noundef %count_multiple.0.lcssa) #3 + tail call void @use(ptr noundef nonnull %str.addr.0.lcssa) #3 + tail call void @use(ptr noundef %foo.addr.0.lcssa) #3 + ret void +} + +; int valid_strlen_index(const char* str) { +; int i = 0; +; while (str[i]) { +; ++i; +; } +; return i; +; } +define i32 @valid_strlen_index(ptr %str) { +; CHECK-LABEL: define i32 @valid_strlen_index( +; CHECK-SAME: ptr [[STR:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[STR]]) +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], %[[WHILE_COND]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[STR]], i64 [[INDVARS_IV]] +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[ARRAYIDX]], align 1 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw nsw i64 [[STRLEN]] to i32 +; CHECK-NEXT: ret i32 [[TMP1]] +; +entry: + br label %while.cond + +while.cond: + %indvars.iv = phi i64 [ %indvars.iv.next, %while.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i8, ptr %str, i64 %indvars.iv + %0 = load i8, ptr %arrayidx, align 1 + %tobool.not = icmp eq i8 %0, 0 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br i1 %tobool.not, label %while.end, label %while.cond + +while.end: + %1 = trunc nuw nsw i64 %indvars.iv to i32 + ret i32 %1 +} + +; void valid_strlen_offset(const my_char* str) { +; if (*(str++) == '\0') return; +; if (*(str++) == '\0') return; +; if (*(str++) == '\0') return; +; while (*str) { +; ++str; +; } +; use(str); +; } +define dso_local void @valid_strlen_offset(ptr noundef %str) local_unnamed_addr { +; CHECK-LABEL: define dso_local void @valid_strlen_offset( +; CHECK-SAME: ptr noundef [[STR:%.*]]) local_unnamed_addr { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[STR]], align 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[IF_END:.*]] +; CHECK: [[IF_END]]: +; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[STR]], i64 1 +; CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[INCDEC_PTR]], align 1 +; CHECK-NEXT: [[CMP4:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[CMP4]], label %[[RETURN]], label %[[IF_END7:.*]] +; CHECK: [[IF_END7]]: +; CHECK-NEXT: [[INCDEC_PTR2:%.*]] = getelementptr inbounds nuw i8, ptr [[STR]], i64 2 +; CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[INCDEC_PTR2]], align 1 +; CHECK-NEXT: [[CMP10:%.*]] = icmp eq i8 [[TMP2]], 0 +; CHECK-NEXT: br i1 [[CMP10]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]] +; CHECK: [[WHILE_COND_PREHEADER]]: +; CHECK-NEXT: [[INCDEC_PTR8:%.*]] = getelementptr i8, ptr [[STR]], i64 3 +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[INCDEC_PTR8]]) +; CHECK-NEXT: [[TMP3:%.*]] = add i64 [[STRLEN]], 3 +; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[STR]], i64 [[TMP3]] +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[INCDEC_PTR14:%.*]], %[[WHILE_COND]] ], [ [[INCDEC_PTR8]], %[[WHILE_COND_PREHEADER]] ] +; CHECK-NEXT: [[TMP4:%.*]] = load i8, ptr [[STR_ADDR_0]], align 1 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i8 [[TMP4]], 0 +; CHECK-NEXT: [[INCDEC_PTR14]] = getelementptr inbounds nuw i8, ptr [[STR_ADDR_0]], i64 1 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: tail call void @use(ptr noundef nonnull [[SCEVGEP]]) +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: ret void +; +entry: + %0 = load i8, ptr %str, align 1 + %cmp = icmp eq i8 %0, 0 + br i1 %cmp, label %return, label %if.end + +if.end: + %incdec.ptr = getelementptr inbounds nuw i8, ptr %str, i64 1 + %1 = load i8, ptr %incdec.ptr, align 1 + %cmp4 = icmp eq i8 %1, 0 + br i1 %cmp4, label %return, label %if.end7 + +if.end7: + %incdec.ptr2 = getelementptr inbounds nuw i8, ptr %str, i64 2 + %2 = load i8, ptr %incdec.ptr2, align 1 + %cmp10 = icmp eq i8 %2, 0 + br i1 %cmp10, label %return, label %while.cond.preheader + +while.cond.preheader: + %incdec.ptr8 = getelementptr inbounds nuw i8, ptr %str, i64 3 + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %incdec.ptr14, %while.cond ], [ %incdec.ptr8, %while.cond.preheader ] + %3 = load i8, ptr %str.addr.0, align 1 + %tobool.not = icmp eq i8 %3, 0 + %incdec.ptr14 = getelementptr inbounds nuw i8, ptr %str.addr.0, i64 1 + br i1 %tobool.not, label %while.end, label %while.cond + +while.end: + tail call void @use(ptr noundef nonnull %str.addr.0) #3 + br label %return + +return: + ret void +} + +; void valid_nested_idiom(const char** strs, int n) { +; for (int i = 0; i < n; ++i) { +; const char* s = strs[i]; +; int count = 0; +; while (*s) { +; ++s; +; ++count; +; } +; usei(count); +; } +; } +define void @valid_nested_idiom(ptr %strs, i32 %n) { +; CHECK-LABEL: define void @valid_nested_idiom( +; CHECK-SAME: ptr [[STRS:%.*]], i32 [[N:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CMP9:%.*]] = icmp sgt i32 [[N]], 0 +; CHECK-NEXT: br i1 [[CMP9]], label %[[FOR_BODY_PREHEADER:.*]], label %[[FOR_COND_CLEANUP:.*]] +; CHECK: [[FOR_BODY_PREHEADER]]: +; CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[N]] to i64 +; CHECK-NEXT: br label %[[FOR_BODY:.*]] +; CHECK: [[FOR_COND_CLEANUP_LOOPEXIT:.*]]: +; CHECK-NEXT: br label %[[FOR_COND_CLEANUP]] +; CHECK: [[FOR_COND_CLEANUP]]: +; CHECK-NEXT: ret void +; CHECK: [[FOR_BODY]]: +; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, %[[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[WHILE_END:.*]] ] +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[STRS]], i64 [[INDVARS_IV]] +; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[TMP0]], align 1 +; CHECK-NEXT: [[TOBOOL_NOT6:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT6]], label %[[WHILE_END]], label %[[WHILE_BODY_PREHEADER:.*]] +; CHECK: [[WHILE_BODY_PREHEADER]]: +; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[TMP0]], i64 1 +; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[SCEVGEP]]) +; CHECK-NEXT: [[TMP2:%.*]] = trunc i64 [[STRLEN]] to i32 +; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], 1 +; CHECK-NEXT: br label %[[WHILE_BODY:.*]] +; CHECK: [[WHILE_BODY]]: +; CHECK-NEXT: [[COUNT_08:%.*]] = phi i32 [ [[INC:%.*]], %[[WHILE_BODY]] ], [ 0, %[[WHILE_BODY_PREHEADER]] ] +; CHECK-NEXT: [[S_07:%.*]] = phi ptr [ [[INCDEC_PTR:%.*]], %[[WHILE_BODY]] ], [ [[TMP0]], %[[WHILE_BODY_PREHEADER]] ] +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[S_07]], i64 1 +; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[COUNT_08]], 1 +; CHECK-NEXT: [[TMP4:%.*]] = load i8, ptr [[INCDEC_PTR]], align 1 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i8 [[TMP4]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END_LOOPEXIT:.*]], label %[[WHILE_BODY]] +; CHECK: [[WHILE_END_LOOPEXIT]]: +; CHECK-NEXT: br label %[[WHILE_END]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[COUNT_0_LCSSA:%.*]] = phi i32 [ 0, %[[FOR_BODY]] ], [ [[TMP3]], %[[WHILE_END_LOOPEXIT]] ] +; CHECK-NEXT: tail call void @usei(i32 [[COUNT_0_LCSSA]]) +; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label %[[FOR_COND_CLEANUP_LOOPEXIT]], label %[[FOR_BODY]] +; +entry: + %cmp9 = icmp sgt i32 %n, 0 + br i1 %cmp9, label %for.body.preheader, label %for.cond.cleanup + +for.body.preheader: + %wide.trip.count = zext nneg i32 %n to i64 + br label %for.body + +for.cond.cleanup: + ret void + +for.body: + %indvars.iv = phi i64 [ 0, %for.body.preheader ], [ %indvars.iv.next, %while.end ] + %arrayidx = getelementptr inbounds ptr, ptr %strs, i64 %indvars.iv + %0 = load ptr, ptr %arrayidx, align 8 + %1 = load i8, ptr %0, align 1 + %tobool.not6 = icmp eq i8 %1, 0 + br i1 %tobool.not6, label %while.end, label %while.body + +while.body: + %count.08 = phi i32 [ %inc, %while.body ], [ 0, %for.body ] + %s.07 = phi ptr [ %incdec.ptr, %while.body ], [ %0, %for.body ] + %incdec.ptr = getelementptr inbounds nuw i8, ptr %s.07, i64 1 + %inc = add nuw nsw i32 %count.08, 1 + %2 = load i8, ptr %incdec.ptr, align 1 + %tobool.not = icmp eq i8 %2, 0 + br i1 %tobool.not, label %while.end, label %while.body + +while.end: + %count.0.lcssa = phi i32 [ 0, %for.body ], [ %inc, %while.body ] + tail call void @usei(i32 %count.0.lcssa) #2 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count + br i1 %exitcond.not, label %for.cond.cleanup, label %for.body +} + +define i64 @invalid_strlen_has_side_effects(ptr %str) { +; CHECK-LABEL: define i64 @invalid_strlen_has_side_effects( +; CHECK-SAME: ptr [[STR:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[TMP0:%.*]] = load volatile i8, ptr [[STR_ADDR_0]], align 1 +; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 1 +; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[STR_ADDR_0_LCSSA:%.*]] = phi ptr [ [[STR_ADDR_0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[STR_ADDR_0_LCSSA]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: ret i64 [[SUB_PTR_SUB]] +; +entry: + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.cond ] + %0 = load volatile i8, ptr %str.addr.0, align 1 + %cmp.not = icmp eq i8 %0, 0 + %incdec.ptr = getelementptr i8, ptr %str.addr.0, i64 1 + br i1 %cmp.not, label %while.end, label %while.cond + +while.end: + %sub.ptr.lhs.cast = ptrtoint ptr %str.addr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + ret i64 %sub.ptr.sub +} + + +define i8 @invalid_exit_phi_scev(ptr %str) { +; CHECK-LABEL: define i8 @invalid_exit_phi_scev( +; CHECK-SAME: ptr [[STR:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[STR_ADDR_0]], align 1 +; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 1 +; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[STR_ADDR_0_LCSSA:%.*]] = phi ptr [ [[STR_ADDR_0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[DOTLCSSA:%.*]] = phi i8 [ [[TMP0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[STR_ADDR_0_LCSSA]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: ret i8 [[DOTLCSSA]] +; +entry: + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.cond ] + %0 = load i8, ptr %str.addr.0, align 1 + %cmp.not = icmp eq i8 %0, 0 + %incdec.ptr = getelementptr i8, ptr %str.addr.0, i64 1 + br i1 %cmp.not, label %while.end, label %while.cond + +while.end: + %sub.ptr.lhs.cast = ptrtoint ptr %str.addr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + + ; %0.lcssa has invalid scev rec {%0} expected to be {%str,+,constant} + ret i8 %0 +} + + + +define i64 @invalid_branch_cond(ptr %str) { +; CHECK-LABEL: define i64 @invalid_branch_cond( +; CHECK-SAME: ptr [[STR:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[STR_ADDR_0]], align 1 +; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 10 +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 1 +; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[STR_ADDR_0_LCSSA:%.*]] = phi ptr [ [[STR_ADDR_0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[STR_ADDR_0_LCSSA]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: ret i64 [[SUB_PTR_SUB]] +; +entry: + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.cond ] + %0 = load i8, ptr %str.addr.0, align 1 + + ; We compare against '\n' instead of '\0' + %cmp.not = icmp eq i8 %0, 10 + + %incdec.ptr = getelementptr i8, ptr %str.addr.0, i64 1 + br i1 %cmp.not, label %while.end, label %while.cond + +while.end: + %sub.ptr.lhs.cast = ptrtoint ptr %str.addr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + ret i64 %sub.ptr.sub +} + +define i64 @invalid_unknown_step_size(ptr %str, i64 %step) { +; CHECK-LABEL: define i64 @invalid_unknown_step_size( +; CHECK-SAME: ptr [[STR:%.*]], i64 [[STEP:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[STR_ADDR_0]], align 1 +; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 [[STEP]] +; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[STR_ADDR_0_LCSSA:%.*]] = phi ptr [ [[STR_ADDR_0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[STR_ADDR_0_LCSSA]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: ret i64 [[SUB_PTR_SUB]] +; +entry: + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.cond ] + %0 = load i8, ptr %str.addr.0, align 1 + %cmp.not = icmp eq i8 %0, 0 + %incdec.ptr = getelementptr i8, ptr %str.addr.0, i64 %step + br i1 %cmp.not, label %while.end, label %while.cond + +while.end: + %sub.ptr.lhs.cast = ptrtoint ptr %str.addr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + ret i64 %sub.ptr.sub +} + +declare ptr @pure(ptr) #0; +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } + +define i64 @invalid_add_rec(ptr %str) { +; CHECK-LABEL: define i64 @invalid_add_rec( +; CHECK-SAME: ptr [[STR:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[STR_ADDR_0:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[INDIRECT:%.*]] = tail call ptr @pure(ptr [[STR_ADDR_0]]) +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[INDIRECT]], align 1 +; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 0 +; CHECK-NEXT: [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 1 +; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[STR_ADDR_0_LCSSA:%.*]] = phi ptr [ [[STR_ADDR_0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[STR_ADDR_0_LCSSA]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: ret i64 [[SUB_PTR_SUB]] +; +entry: + br label %while.cond + +while.cond: + %str.addr.0 = phi ptr [ %str, %entry ], [ %incdec.ptr, %while.cond ] + %indirect = tail call ptr @pure(ptr %str.addr.0) + %0 = load i8, ptr %indirect, align 1 + %cmp.not = icmp eq i8 %0, 0 + %incdec.ptr = getelementptr i8, ptr %str.addr.0, i64 1 + br i1 %cmp.not, label %while.end, label %while.cond + +while.end: + %sub.ptr.lhs.cast = ptrtoint ptr %str.addr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %str to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + ret i64 %sub.ptr.sub +} + diff --git a/llvm/test/Transforms/LoopIdiom/wcslen16.ll b/llvm/test/Transforms/LoopIdiom/wcslen16.ll new file mode 100644 index 0000000000000..d3b0b8d208cd8 --- /dev/null +++ b/llvm/test/Transforms/LoopIdiom/wcslen16.ll @@ -0,0 +1,126 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes='loop(loop-idiom),verify' < %s -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i64 @valid_strlen16(ptr %src) { +; CHECK-LABEL: define i64 @valid_strlen16( +; CHECK-SAME: ptr [[SRC:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null +; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]] +; CHECK: [[LOR_LHS_FALSE]]: +; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[SRC]], align 2 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i16 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]] +; CHECK: [[WHILE_COND_PREHEADER]]: +; CHECK-NEXT: [[NEWGEP:%.*]] = getelementptr i8, ptr [[SRC]], i64 2 +; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[NEWGEP]]) +; CHECK-NEXT: [[TMP1:%.*]] = shl i64 [[WCSLEN]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 2 +; CHECK-NEXT: [[END:%.*]] = getelementptr i8, ptr [[SRC]], i64 [[TMP2]] +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ] +; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 2 +; CHECK-NEXT: [[TMP3:%.*]] = load i16, ptr [[CURR_0]], align 2 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i16 [[TMP3]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[END]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 1 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i64 [[RETVAL_0]] +; +entry: + %cmp = icmp eq ptr %src, null + br i1 %cmp, label %return, label %lor.lhs.false + +lor.lhs.false: ; preds = %entry + %0 = load i16, ptr %src, align 2 + %cmp1 = icmp eq i16 %0, 0 + br i1 %cmp1, label %return, label %while.cond + +while.cond: ; preds = %lor.lhs.false, %while.cond + %src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %lor.lhs.false ] + %curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 2 + %1 = load i16, ptr %curr.0, align 2 + %tobool.not = icmp eq i16 %1, 0 + br i1 %tobool.not, label %while.end, label %while.cond + +while.end: ; preds = %while.cond + %sub.ptr.lhs.cast = ptrtoint ptr %curr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %src to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + %sub.ptr.div = ashr exact i64 %sub.ptr.sub, 1 + br label %return + +return: ; preds = %entry, %lor.lhs.false, %while.end + %retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ] + ret i64 %retval.0 +} + +define i64 @invalid_char_size(ptr %src) { +; CHECK-LABEL: define i64 @invalid_char_size( +; CHECK-SAME: ptr [[SRC:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null +; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]] +; CHECK: [[LOR_LHS_FALSE]]: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SRC]], align 2 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]] +; CHECK: [[WHILE_COND_PREHEADER]]: +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ] +; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 4 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[CURR_0]], align 4 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[CURR_0_LCSSA:%.*]] = phi ptr [ [[CURR_0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[CURR_0_LCSSA]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i64 [[RETVAL_0]] +; +entry: + %cmp = icmp eq ptr %src, null + br i1 %cmp, label %return, label %lor.lhs.false + +lor.lhs.false: ; preds = %entry + %0 = load i32, ptr %src, align 2 + %cmp1 = icmp eq i32 %0, 0 + br i1 %cmp1, label %return, label %while.cond + +while.cond: ; preds = %lor.lhs.false, %while.cond + %src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %lor.lhs.false ] + %curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 4 + %1 = load i32, ptr %curr.0, align 4 + %tobool.not = icmp eq i32 %1, 0 + br i1 %tobool.not, label %while.end, label %while.cond + +while.end: ; preds = %while.cond + %sub.ptr.lhs.cast = ptrtoint ptr %curr.0 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %src to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + %sub.ptr.div = ashr exact i64 %sub.ptr.sub, 2 + br label %return + +return: ; preds = %entry, %lor.lhs.false, %while.end + %retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ] + ret i64 %retval.0 +} +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"wchar_size", i32 2} + diff --git a/llvm/test/Transforms/LoopIdiom/wcslen32.ll b/llvm/test/Transforms/LoopIdiom/wcslen32.ll new file mode 100644 index 0000000000000..488afff86c245 --- /dev/null +++ b/llvm/test/Transforms/LoopIdiom/wcslen32.ll @@ -0,0 +1,134 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes='loop(loop-idiom),verify' < %s -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i64 @valid_wcslen32(ptr %src) { +; CHECK-LABEL: define i64 @valid_wcslen32( +; CHECK-SAME: ptr [[SRC:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null +; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]] +; CHECK: [[LOR_LHS_FALSE]]: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SRC]], align 4 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]] +; CHECK: [[WHILE_COND_PREHEADER]]: +; CHECK-NEXT: [[NEWGEP:%.*]] = getelementptr i8, ptr [[SRC]], i64 4 +; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[NEWGEP]]) +; CHECK-NEXT: [[TMP1:%.*]] = shl i64 [[WCSLEN]], 2 +; CHECK-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 4 +; CHECK-NEXT: [[END:%.*]] = getelementptr i8, ptr [[SRC]], i64 [[TMP2]] +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ] +; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 4 +; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[CURR_0]], align 4 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP3]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[END]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i64 [[RETVAL_0]] +; +entry: + %cmp = icmp eq ptr %src, null + br i1 %cmp, label %return, label %lor.lhs.false + +lor.lhs.false: ; preds = %entry + %0 = load i32, ptr %src, align 4 + %cmp1 = icmp eq i32 %0, 0 + br i1 %cmp1, label %return, label %while.cond.preheader + +while.cond.preheader: ; preds = %lor.lhs.false + br label %while.cond + +while.cond: ; preds = %while.cond.preheader, %while.cond + %src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %while.cond.preheader ] + %curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 4 + %1 = load i32, ptr %curr.0, align 4 + %tobool.not = icmp eq i32 %1, 0 + br i1 %tobool.not, label %while.end, label %while.cond + +while.end: ; preds = %while.cond + %curr.0.lcssa = phi ptr [ %curr.0, %while.cond ] + %sub.ptr.lhs.cast = ptrtoint ptr %curr.0.lcssa to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %src to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + %sub.ptr.div = ashr exact i64 %sub.ptr.sub, 2 + br label %return + +return: ; preds = %entry, %lor.lhs.false, %while.end + %retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ] + ret i64 %retval.0 +} + +define i64 @invalid_char_size(ptr %src) { +; CHECK-LABEL: define i64 @invalid_char_size( +; CHECK-SAME: ptr [[SRC:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null +; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]] +; CHECK: [[LOR_LHS_FALSE]]: +; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[SRC]], align 2 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i16 [[TMP0]], 0 +; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]] +; CHECK: [[WHILE_COND_PREHEADER]]: +; CHECK-NEXT: br label %[[WHILE_COND:.*]] +; CHECK: [[WHILE_COND]]: +; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ] +; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 2 +; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[CURR_0]], align 2 +; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i16 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]] +; CHECK: [[WHILE_END]]: +; CHECK-NEXT: [[CURR_0_LCSSA:%.*]] = phi ptr [ [[CURR_0]], %[[WHILE_COND]] ] +; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[CURR_0_LCSSA]] to i64 +; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64 +; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 1 +; CHECK-NEXT: br label %[[RETURN]] +; CHECK: [[RETURN]]: +; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i64 [[RETVAL_0]] +; +entry: + %cmp = icmp eq ptr %src, null + br i1 %cmp, label %return, label %lor.lhs.false + +lor.lhs.false: ; preds = %entry + %0 = load i16, ptr %src, align 2 + %cmp1 = icmp eq i16 %0, 0 + br i1 %cmp1, label %return, label %while.cond.preheader + +while.cond.preheader: ; preds = %lor.lhs.false + br label %while.cond + +while.cond: ; preds = %while.cond.preheader, %while.cond + %src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %while.cond.preheader ] + %curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 2 + %1 = load i16, ptr %curr.0, align 2 + %tobool.not = icmp eq i16 %1, 0 + br i1 %tobool.not, label %while.end, label %while.cond + +while.end: ; preds = %while.cond + %curr.0.lcssa = phi ptr [ %curr.0, %while.cond ] + %sub.ptr.lhs.cast = ptrtoint ptr %curr.0.lcssa to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %src to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + %sub.ptr.div = ashr exact i64 %sub.ptr.sub, 1 + br label %return + +return: ; preds = %entry, %lor.lhs.false, %while.end + %retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ] + ret i64 %retval.0 +} +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"wchar_size", i32 4} +