[llvm] [Transforms] LoopIdiomRecognize recognize strlen8 (PR #107733)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 17 07:55:09 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Henry Jiang (mustartt)
<details>
<summary>Changes</summary>
This PR continues the effort made in https://discourse.llvm.org/t/rfc-strlen-loop-idiom-recognition-folding/55848 and https://reviews.llvm.org/D83392 and https://reviews.llvm.org/D88460 to extend `LoopIdiomRecognize` to find and replace loops of the form
```c
base = str;
while (*str)
++str;
```
and transforming the `strlen` loop idiom into the appropriate `strlen` and `wcslen` library call which will give a small performance boost if replaced.
```c
str = base + strlen(base)
len = str - base
```
---
Patch is 32.59 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/107733.diff
7 Files Affected:
- (modified) llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h (+6)
- (modified) llvm/include/llvm/Transforms/Utils/BuildLibCalls.h (+6)
- (modified) llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp (+251-4)
- (modified) llvm/lib/Transforms/Utils/BuildLibCalls.cpp (+9)
- (added) llvm/test/Transforms/LoopIdiom/strlen.ll (+293)
- (added) llvm/test/Transforms/LoopIdiom/wcslen16.ll (+66)
- (added) llvm/test/Transforms/LoopIdiom/wcslen32.ll (+70)
``````````diff
diff --git a/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h b/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h
index 0c6406d8618518..241a3fc1093607 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 1979c4af770b02..dfe3b35bb596e1 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 578d087e470e1e..c3cf24686d0934 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -33,6 +33,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 +98,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 formed from loop loads");
STATISTIC(
NumShiftUntilBitTest,
"Number of uncountable loops recognized as 'shift until bitttest' idiom");
@@ -126,6 +128,22 @@ static cl::opt<bool, true>
cl::location(DisableLIRP::Memcpy), cl::init(false),
cl::ReallyHidden);
+bool DisableLIRP::Strlen;
+static cl::opt<bool, true>
+ DisableLIRPStrlen("disable-" DEBUG_TYPE "-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<bool, true>
+ DisableLIRPWcslen("disable-" DEBUG_TYPE "-wcslen",
+ cl::desc("Proceed with loop idiom recognize pass, but do "
+ "not convert loop(s) to wcslen."),
+ cl::location(DisableLIRP::Wcslen), cl::init(false),
+ cl::ReallyHidden);
+
static cl::opt<bool> UseLIRCodeSizeHeurs(
"use-lir-code-size-heurs",
cl::desc("Use loop idiom recognition code size heuristics when compiling"
@@ -246,6 +264,7 @@ class LoopIdiomRecognize {
bool recognizeShiftUntilBitTest();
bool recognizeShiftUntilZero();
+ bool recognizeAndInsertStrLen();
/// @}
};
@@ -1489,7 +1508,7 @@ bool LoopIdiomRecognize::runOnNoncountableLoop() {
return recognizePopcount() || recognizeAndInsertFFS() ||
recognizeShiftUntilBitTest() || recognizeShiftUntilZero() ||
- recognizeShiftUntilLessThan();
+ recognizeShiftUntilLessThan() || recognizeAndInsertStrLen();
}
/// Check if the given conditional branch is based on the comparison between
@@ -1507,9 +1526,11 @@ static Value *matchCondition(BranchInst *BI, BasicBlock *LoopEntry,
if (!Cond)
return nullptr;
- ConstantInt *CmpZero = dyn_cast<ConstantInt>(Cond->getOperand(1));
- if (!CmpZero || !CmpZero->isZero())
- return nullptr;
+ if (!isa<ConstantPointerNull>(Cond->getOperand(1))) {
+ ConstantInt *CmpZero = dyn_cast<ConstantInt>(Cond->getOperand(1));
+ if (!CmpZero || !CmpZero->isZero())
+ return nullptr;
+ }
BasicBlock *TrueSucc = BI->getSuccessor(0);
BasicBlock *FalseSucc = BI->getSuccessor(1);
@@ -1524,6 +1545,232 @@ static Value *matchCondition(BranchInst *BI, BasicBlock *LoopEntry,
return nullptr;
}
+/// Recognizes a strlen idiom by checking for loops that increment
+/// a char pointer and then subtract with the base pointer.
+///
+/// If detected, transforms the relevant code to a strlen function
+/// call, and returns true; otherwise, returns false.
+///
+/// The core idiom we are trying to detect is:
+/// \code
+/// start = str;
+/// do {
+/// str++;
+/// } while(*str != '\0');
+/// \endcode
+///
+/// The transformed output is similar to below c-code:
+/// \code
+/// str = start + strlen(start)
+/// len = str - start
+/// \endcode
+///
+/// Later the pointer subtraction will be folded by InstCombine
+bool LoopIdiomRecognize::recognizeAndInsertStrLen() {
+ if (DisableLIRPStrlen)
+ return false;
+
+ // Give up if the loop has multiple blocks or multiple backedges.
+ if (CurLoop->getNumBackEdges() != 1 || CurLoop->getNumBlocks() != 1)
+ return false;
+
+ // It should have a preheader containing nothing but an unconditional branch.
+ auto *Preheader = CurLoop->getLoopPreheader();
+ if (!Preheader || &Preheader->front() != Preheader->getTerminator())
+ return false;
+
+ auto *EntryBI = dyn_cast<BranchInst>(Preheader->getTerminator());
+ if (!EntryBI || EntryBI->isConditional())
+ return false;
+
+ // The loop exit must be conditioned on an icmp with 0.
+ // The icmp operand has to be a load on some SSA reg that increments
+ // by 1 in the loop.
+ BasicBlock *LoopBody = *CurLoop->block_begin();
+ BranchInst *LoopTerm = dyn_cast<BranchInst>(LoopBody->getTerminator());
+ Value *LoopCond = matchCondition(LoopTerm, LoopBody);
+
+ if (!LoopCond)
+ return false;
+
+ auto *LoopLoad = dyn_cast<LoadInst>(LoopCond);
+ if (!LoopLoad || LoopLoad->getPointerAddressSpace() != 0)
+ return false;
+
+ Type *OperandType = LoopLoad->getType();
+ if (!OperandType || !OperandType->isIntegerTy())
+ return false;
+
+ // See if the pointer expression is an AddRec with step 1 ({n,+,1}) on
+ // the loop, indicating strlen calculation.
+ auto *IncPtr = LoopLoad->getPointerOperand();
+ const SCEVAddRecExpr *LoadEv = dyn_cast<SCEVAddRecExpr>(SE->getSCEV(IncPtr));
+
+ if (!LoadEv || LoadEv->getLoop() != CurLoop || !LoadEv->isAffine())
+ return false;
+
+ const SCEVConstant *Step =
+ dyn_cast<SCEVConstant>(LoadEv->getStepRecurrence(*SE));
+ if (!Step)
+ return false;
+
+ unsigned int StepSize = 0;
+ if (ConstantInt *CI = dyn_cast<ConstantInt>(Step->getValue()))
+ StepSize = CI->getZExtValue();
+
+ unsigned 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 || DisableLIRPWcslen)
+ return false;
+
+ // Scan every instruction in the loop to ensure there are no side effects.
+ for (auto &I : *LoopBody)
+ if (I.mayHaveSideEffects())
+ return false;
+
+ auto *LoopExitBB = CurLoop->getExitBlock();
+ if (!LoopExitBB)
+ return false;
+
+ // Check that the loop exit block is valid:
+ // It needs to have exactly one LCSSA Phi which is an AddRec.
+ PHINode *LCSSAPhi = nullptr;
+ for (PHINode &PN : LoopExitBB->phis()) {
+ if (!LCSSAPhi && PN.getNumIncomingValues() == 1)
+ LCSSAPhi = &PN;
+ else
+ return false;
+ }
+
+ if (!LCSSAPhi || !SE->isSCEVable(LCSSAPhi->getType()))
+ return false;
+
+ // This matched the pointer version of the idiom
+ if (LCSSAPhi->getIncomingValueForBlock(LoopBody) !=
+ LoopLoad->getPointerOperand())
+ return false;
+
+ const SCEVAddRecExpr *LCSSAEv =
+ dyn_cast<SCEVAddRecExpr>(SE->getSCEV(LCSSAPhi->getIncomingValue(0)));
+
+ if (!LCSSAEv || !dyn_cast<SCEVUnknown>(SE->getPointerBase(LCSSAEv)) ||
+ !LCSSAEv->isAffine())
+ return false;
+
+ // We can now expand the base of the str
+ IRBuilder<> Builder(Preheader->getTerminator());
+
+ auto LoopPhiRange = LoopBody->phis();
+ if (!hasNItems(LoopPhiRange, 1))
+ return false;
+ auto *LoopPhi = &*LoopPhiRange.begin();
+ Value *PreVal = LoopPhi->getIncomingValueForBlock(Preheader);
+ if (!PreVal)
+ return false;
+
+ Value *Expanded = nullptr;
+ Type *ExpandedType = nullptr;
+ if (auto *GEP = dyn_cast<GetElementPtrInst>(LoopLoad->getPointerOperand())) {
+ if (GEP->getPointerOperand() != LoopPhi)
+ return false;
+ GetElementPtrInst *NewGEP = GetElementPtrInst::Create(
+ LoopLoad->getType(), PreVal, SmallVector<Value *, 4>(GEP->indices()),
+ "newgep", Preheader->getTerminator());
+ Expanded = NewGEP;
+ ExpandedType = LoopLoad->getType();
+ } else if (LoopLoad->getPointerOperand() == LoopPhi) {
+ Expanded = PreVal;
+ ExpandedType = LoopLoad->getType();
+ }
+ if (!Expanded)
+ return false;
+
+ // Ensure that the GEP has the correct index if the pointer was modified.
+ // This can happen when the pointer in the user code, outside the loop,
+ // walks past a certain pre-checked index of the string.
+ if (auto *GEP = dyn_cast<GEPOperator>(Expanded)) {
+ if (GEP->getNumOperands() != 2)
+ return false;
+
+ ConstantInt *I0 = dyn_cast<ConstantInt>(GEP->getOperand(1));
+ if (!I0)
+ return false;
+
+ int64_t Index = I0->getSExtValue(); // GEP index
+ auto *SAdd = dyn_cast<SCEVAddExpr>(LoadEv->getStart());
+ if (!SAdd || SAdd->getNumOperands() != 2)
+ return false;
+
+ auto *SAdd0 = dyn_cast<SCEVConstant>(SAdd->getOperand(0));
+ if (!SAdd0)
+ return false;
+
+ ConstantInt *CInt = SAdd0->getValue(); // SCEV index
+ assert(CInt && "Expecting CInt to be valid.");
+ int64_t Offset = CInt->getSExtValue();
+
+ // Update the index based on the Offset
+ assert((Offset * 8) % GEP->getSourceElementType()->getIntegerBitWidth() ==
+ 0 &&
+ "Invalid offset");
+ int64_t NewIndex =
+ (Offset * 8) / GEP->getSourceElementType()->getIntegerBitWidth() -
+ Index;
+ Value *NewIndexVal =
+ ConstantInt::get(GEP->getOperand(1)->getType(), NewIndex);
+ GEP->setOperand(1, NewIndexVal);
+ }
+
+ Value *StrLenFunc = nullptr;
+ switch (OpWidth) {
+ case 8:
+ if (!TLI->has(LibFunc_strlen))
+ return false;
+ StrLenFunc = emitStrLen(Expanded, Builder, *DL, TLI);
+ break;
+ case 16:
+ case 32:
+ if (!TLI->has(LibFunc_wcslen))
+ return false;
+ StrLenFunc = emitWcsLen(Expanded, Builder, *DL, TLI);
+ }
+
+ assert(StrLenFunc && "Failed to emit strlen function.");
+
+ // Replace LCSSA Phi use with new pointer to the null terminator
+ SmallVector<Value *, 4> NewBaseIndex{StrLenFunc};
+ GetElementPtrInst *NewEndPtr = GetElementPtrInst::Create(
+ ExpandedType, Expanded, NewBaseIndex, "end", Preheader->getTerminator());
+ LCSSAPhi->replaceAllUsesWith(NewEndPtr);
+ RecursivelyDeleteDeadPHINode(LCSSAPhi);
+
+ ConstantInt *NewLoopCond = LoopTerm->getSuccessor(0) == LoopBody
+ ? Builder.getFalse()
+ : Builder.getTrue();
+ LoopTerm->setCondition(NewLoopCond);
+
+ deleteDeadInstruction(cast<Instruction>(LoopCond));
+ deleteDeadInstruction(cast<Instruction>(IncPtr));
+ SE->forgetLoop(CurLoop);
+
+ LLVM_DEBUG(dbgs() << " Formed strlen: " << *StrLenFunc << "\n");
+
+ ORE.emit([&]() {
+ return OptimizationRemark(DEBUG_TYPE, "recognizeAndInsertStrLen",
+ CurLoop->getStartLoc(), Preheader)
+ << "Transformed pointer difference into a call to strlen() function";
+ });
+
+ ++NumStrLen;
+
+ 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 b0da19813f0a4b..e880d8fc5dd8df 100644
--- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
@@ -1506,6 +1506,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.ll b/llvm/test/Transforms/LoopIdiom/strlen.ll
new file mode 100644
index 00000000000000..43ed9d0980bc49
--- /dev/null
+++ b/llvm/test/Transforms/LoopIdiom/strlen.ll
@@ -0,0 +1,293 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='loop-idiom' < %s -S | FileCheck %s
+
+declare void @use(ptr)
+
+define i64 @valid_strlen_1(ptr %0) {
+; CHECK-LABEL: define i64 @valid_strlen_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[TMP0]])
+; CHECK-NEXT: [[DOTLCSSA:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[STRLEN]]
+; CHECK-NEXT: br label %[[BB2:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: [[TMP8:%.*]] = icmp eq i8 poison, 0
+; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr poison, i64 1
+; CHECK-NEXT: br i1 true, label %[[BB5:.*]], label %[[BB2]]
+; CHECK: [[BB5]]:
+; CHECK-NEXT: [[TMP12:%.*]] = ptrtoint ptr [[DOTLCSSA]] to i64
+; CHECK-NEXT: [[TMP13:%.*]] = ptrtoint ptr [[TMP0]] to i64
+; CHECK-NEXT: [[TMP14:%.*]] = sub i64 [[TMP12]], [[TMP13]]
+; CHECK-NEXT: ret i64 [[TMP14]]
+;
+ br label %2
+
+2: ; preds = %2, %1
+ %3 = phi ptr [ %0, %1 ], [ %6, %2 ]
+ %4 = load i8, ptr %3, align 1
+ %5 = icmp eq i8 %4, 0
+ %6 = getelementptr inbounds i8, ptr %3, i64 1
+ br i1 %5, label %7, label %2
+
+7: ; preds = %2
+ %8 = ptrtoint ptr %3 to i64
+ %9 = ptrtoint ptr %0 to i64
+ %10 = sub i64 %8, %9
+ ret i64 %10
+}
+
+
+define i32 @valid_strlen_2(ptr %0) {
+; CHECK-LABEL: define i32 @valid_strlen_2(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; CHECK-NEXT: br i1 [[TMP2]], label %[[BB14:.*]], label %[[BB3:.*]]
+; CHECK: [[BB3]]:
+; CHECK-NEXT: [[TMP4:%.*]] = load i8, ptr [[TMP0]], align 1
+; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i8 [[TMP4]], 0
+; CHECK-NEXT: br i1 [[TMP5]], label %[[BB14]], label %[[DOTPREHEADER:.*]]
+; CHECK: [[_PREHEADER:.*:]]
+; CHECK-NEXT: [[STR:%.*]] = getelementptr i8, ptr [[TMP0]], i64 0
+; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[STR]])
+; CHECK-NEXT: [[STR_ADDR_0_LCSSA:%.*]] = getelementptr i8, ptr [[STR]], i64 [[STRLEN]]
+; CHECK-NEXT: br label %[[BB6:.*]]
+; CHECK: [[BB6]]:
+; CHECK-NEXT: [[TMP7:%.*]] = phi ptr [ poison, %[[BB6]] ], [ [[TMP0]], %[[DOTPREHEADER]] ]
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 poison, 0
+; CHECK-NEXT: br i1 true, label %[[BB9:.*]], label %[[BB6]]
+; CHECK: [[BB9]]:
+; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[STR_ADDR_0_LCSSA]] to i64
+; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64
+; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
+; CHECK-NEXT: [[TMP13:%.*]] = trunc i64 [[SUB_PTR_SUB]] to i32
+; CHECK-NEXT: br label %[[BB14]]
+; CHECK: [[BB14]]:
+; CHECK-NEXT: [[TMP15:%.*]] = phi i32 [ [[TMP13]], %[[BB9]] ], [ 0, %[[BB3]] ], [ 0, [[TMP1:%.*]] ]
+; CHECK-NEXT: ret i32 [[TMP15]]
+;
+ %2 = icmp eq ptr %0, null
+ br i1 %2, label %16, label %3
+
+3: ; preds = %1
+ %4 = load i8, ptr %0, align 1
+ %5 = icmp eq i8 %4, 0
+ br i1 %5, label %16, label %6
+
+6: ; preds = %3, %6
+ %7 = phi ptr [ %8, %6 ], [ %0, %3 ]
+ %8 = getelementptr inbounds i8, ptr %7, i64 1
+ %9 = load i8, ptr %8, align 1
+ %10 = icmp eq i8 %9, 0
+ br i1 %10, label %11, label %6
+
+11: ; preds = %6
+ %12 = ptrtoint ptr %8 to i64
+ %13 = ptrtoint ptr %0 to i64
+ %14 = sub i64 %12, %13
+ %15 = trunc i64 %14 to i32
+ br label %16
+
+16: ; preds = %1, %3, %11
+ %17 = phi i32 [ %15, %11 ], [ 0, %3 ], [ 0, %1 ]
+ ret i32 %17
+}
+
+define i64 @valid_strlen_3(ptr %str) local_unnamed_addr #0 {
+; CHECK-LABEL: define i64 @valid_strlen_3(
+; CHECK-SAME: ptr [[STR:%.*]]) local_unnamed_addr {
+; CHECK-NEXT: [[_PREHEADER:.*:]]
+; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[STR]])
+; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[STR]], i64 [[STRLEN]]
+; CHECK-NEXT: br label %[[WHILE_COND:.*]]
+; CHECK: [[WHILE_COND]]:
+; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i8 poison, 0
+; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i8, ptr poison, i64 1
+; CHECK-NEXT: br i1 true, label %[[WHILE_END:.*]], label %[[WHILE_COND]]
+; CHECK: [[WHILE_END]]:
+; CHECK-NEXT: [[TMP10:%.*]] = ptrtoint ptr [[TMP0]] to i64
+; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64
+; CHECK-NEXT: [[TMP13:%.*]] = sub i64 [[TMP10]], [[SUB_PTR_RHS_CAST]]
+; CHECK-NEXT: tail call void @use(ptr [[TMP0]])
+; CHECK-NEXT: tail call void @use(ptr [[STR]])
+; CHECK-NEXT: ret i64 [[TMP13]]
+;
+entry:
+ br label %while.cond
+
+while.cond: ; preds = %while.cond, %entry
+ %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 inbounds i8, ptr %str.addr.0, i64 1
+ br i1 %cmp.not, label %while.end, label %while.cond
+
+while.end: ; preds = %while.cond
+ %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
+ tail call void @use(ptr %str.addr.0)
+ tail call void @use(ptr %str)
+ ret i64 %sub.ptr.sub
+}
+
+define i64 @valid_strlen_4(ptr %0) {
+; CHECK-LABEL: define i64 @valid_strlen_4(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; CHECK-NEXT: br i1 [[TMP2]], label %[[BB10:.*]], label %[[DOTPREHEADER:.*]]
+; CHECK: [[_PREHEADER:.*:]]
+; CHECK-NEXT: [[NEWGEP:%.*]] = getelementptr i8, ptr [[TMP0]], i64 0
+; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[NEWGEP]])
+; CHECK-NEXT: [[END:%.*]] = getelementptr i8, ptr [[NEWGEP]], i64 [[STRLEN]]
+; CHECK-NEXT: br label %[[BB3:.*]]
+; CHECK: [[BB3]]:
+; CHECK-NEXT: [[TMP4:%.*]] = phi ptr [ poison, %[[BB3]] ], [ [[TMP0]], %[[DOTPREHEADER]] ]
+; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i8 poison, 0
+; CHECK-NEXT: br i1 true, label %[[BB6:.*]], label %[[BB3]]
+; CHECK: [[BB6]]:
+; CHECK-NEXT: [[TMP7:%.*]] = ptrtoint ptr [[END]] to i64
+; CHECK-NEXT: [[TMP8:%.*]] = ptrtoint ptr [[TMP0]] to i64
+; CHECK-NEXT: [[TMP9:%.*]] = sub i64 [[TMP7]], [[TMP8]]
+; CHECK-NEXT: br label %[[BB10]]
+; CHECK: [[BB10]]:
+; CHECK-NEXT: [[TMP11:%.*]] = phi i64 [ ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/107733
More information about the llvm-commits
mailing list