[llvm] [Transforms] LoopIdiomRecognize recognize strlen8 (PR #107733)

Henry Jiang via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 8 10:17:47 PDT 2024


https://github.com/mustartt updated https://github.com/llvm/llvm-project/pull/107733

>From af63aa747d8283640a0b6a881b6e2656ff238674 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry.jiang1 at ibm.com>
Date: Sat, 7 Sep 2024 22:27:50 -0400
Subject: [PATCH 1/2] Initial upstreaming of strlen8 LIR 1 out of 3

---
 .../Transforms/Scalar/LoopIdiomRecognize.h    |   3 +
 .../Transforms/Scalar/LoopIdiomRecognize.cpp  | 296 +++++++++++++++++-
 llvm/test/Transforms/LoopIdiom/strlen.ll      | 149 +++++++++
 3 files changed, 445 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/Transforms/LoopIdiom/strlen.ll

diff --git a/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h b/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h
index 0c6406d8618518..3a9f016ce9bd60 100644
--- a/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h
+++ b/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h
@@ -34,6 +34,9 @@ struct DisableLIRP {
 
   /// When true, Memcpy is disabled.
   static bool Memcpy;
+
+  /// When true, Strlen is disabled.
+  static bool Strlen;
 };
 
 /// Performs Loop Idiom Recognize Pass.
diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index 578d087e470e1e..38cb79d7820c68 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -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 formed from loop loads");
 STATISTIC(
     NumShiftUntilBitTest,
     "Number of uncountable loops recognized as 'shift until bitttest' idiom");
@@ -126,6 +127,14 @@ 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);
+
 static cl::opt<bool> UseLIRCodeSizeHeurs(
     "use-lir-code-size-heurs",
     cl::desc("Use loop idiom recognition code size heuristics when compiling"
@@ -246,6 +255,7 @@ class LoopIdiomRecognize {
 
   bool recognizeShiftUntilBitTest();
   bool recognizeShiftUntilZero();
+  bool recognizeAndInsertStrLen();
 
   /// @}
 };
@@ -1507,9 +1517,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 +1536,284 @@ static Value *matchCondition(BranchInst *BI, BasicBlock *LoopEntry,
   return nullptr;
 }
 
+/// getCandidateResInstr - If there is strlen calculated, return the Result
+/// instruction based on the \p OpWidth passed, else return nullptr
+static Instruction *getCandidateResInstr(Instruction *EndAddress,
+                                         Value *StartAddress,
+                                         unsigned OpWidth) {
+  using namespace llvm::PatternMatch;
+
+  assert(StartAddress && "Valid start address required.");
+
+  // lambda expression to check that the instruction has a single user
+  auto GetSingleUser = [](Instruction *I) -> User * {
+    if (I->hasOneUse())
+      return *I->user_begin();
+    return nullptr;
+  };
+
+  // The pointer to the end address should only have one use which is a pointer
+  // to int instruction.
+  auto *TmpUser = GetSingleUser(EndAddress);
+  if (!TmpUser)
+    return nullptr;
+
+  if (PtrToIntInst *PToI = dyn_cast<PtrToIntInst>(TmpUser)) {
+    // The only user of the PtrToIntInst should be the sub instruction that
+    // calculates the difference b/w the two pointer operands.
+    TmpUser = GetSingleUser(PToI);
+    if (!TmpUser)
+      return nullptr;
+    Instruction *Inst = dyn_cast<Instruction>(TmpUser);
+
+    if (!Inst || Inst->getOpcode() != Instruction::Sub ||
+        Inst->getOperand(0) != PToI)
+      return nullptr;
+    Value *MatchAddr;
+    if (match(Inst->getOperand(1), m_PtrToInt(m_Value(MatchAddr)))) {
+      if (MatchAddr != StartAddress)
+        return nullptr;
+
+      // We found the candidate sub instruction
+      switch (OpWidth) {
+      case 8:
+        return Inst;
+      default:
+        return nullptr;
+      }
+    }
+  }
+
+  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
+///     if (str == NULL)
+///       goto loop-exit // the precondition of the loop
+///     start = str;
+///     do {
+///       str++;
+///     } while(*str!='\0');
+///     return (str - start);
+/// loop-exit:
+/// \endcode
+///
+/// The transformed output is similar to below c-code:
+/// \code
+///     if (str == NULL)
+///       goto loop-exit // the precondition of the loop
+///     return strlen(str);
+/// \endcode
+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 *Pre = CurLoop->getLoopPreheader();
+  if (!Pre || &Pre->front() != Pre->getTerminator())
+    return false;
+
+  auto *EntryBI = dyn_cast<BranchInst>(Pre->getTerminator());
+  if (!EntryBI || EntryBI->isConditional())
+    return false;
+
+  // It should have a precondition block
+  auto *PreCondBB = Pre->getSinglePredecessor();
+  if (!PreCondBB)
+    return false;
+
+  // The precondition terminator instruction should skip the loop body based on
+  // an icmp with zero/null.
+  if (!matchCondition(dyn_cast<BranchInst>(PreCondBB->getTerminator()), Pre))
+    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.
+  auto *LoopBody = *(CurLoop->block_begin());
+  auto *LoopTerm = dyn_cast<BranchInst>(LoopBody->getTerminator());
+  auto *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 ConstIntValue = 0;
+  if (ConstantInt *CI = dyn_cast<ConstantInt>(Step->getValue()))
+    ConstIntValue = CI->getZExtValue();
+
+  unsigned OpWidth = OperandType->getIntegerBitWidth();
+  if (OpWidth != ConstIntValue * 8)
+    return false;
+  if (OpWidth != 8)
+    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;
+
+  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(Pre->getTerminator());
+
+  PHINode *LoopPhi = &*LoopBody->phis().begin();
+  if (!LoopPhi || ++LoopBody->phis().begin() != LoopBody->phis().end())
+    return false;
+  Value *PreVal = LoopBody->phis().begin()->getIncomingValueForBlock(Pre);
+  if (!PreVal)
+    return false;
+
+  Value *Expanded = nullptr;
+  if (auto *GEP = dyn_cast<GetElementPtrInst>(LoopLoad->getPointerOperand())) {
+    if (GEP->getPointerOperand() != LoopPhi)
+      return false;
+    GetElementPtrInst *NewGEP =
+        GetElementPtrInst::Create(GEP->getSourceElementType(), PreVal,
+                                  SmallVector<Value *, 4>(GEP->indices()),
+                                  "newgep", Pre->getTerminator());
+    Expanded = NewGEP;
+  } else if (LoopLoad->getPointerOperand() == LoopPhi)
+    Expanded = PreVal;
+  if (!Expanded)
+    return false;
+
+  // Check that the LoopExitBB is calculating the string length and identify
+  // the instruction that has the string length calculation
+  Instruction *ResInst = getCandidateResInstr(LCSSAPhi, PreVal, OpWidth);
+  if (!ResInst)
+    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:
+    StrLenFunc = emitStrLen(Expanded, Builder, *DL, TLI);
+    break;
+  }
+
+  assert(StrLenFunc && "Failed to emit strlen function.");
+
+  // Replace the subtraction instruction by the result of strlen
+  ResInst->replaceAllUsesWith(StrLenFunc);
+
+  // Remove the loop-exit branch and delete dead instructions
+  RecursivelyDeleteTriviallyDeadInstructions(ResInst, TLI);
+
+  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(), Pre)
+           << "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/test/Transforms/LoopIdiom/strlen.ll b/llvm/test/Transforms/LoopIdiom/strlen.ll
new file mode 100644
index 00000000000000..641fce0da8b785
--- /dev/null
+++ b/llvm/test/Transforms/LoopIdiom/strlen.ll
@@ -0,0 +1,149 @@
+; RUN: opt -passes='loop-idiom' < %s -S | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-n32:64"
+target triple = "powerpc64le-unknown-linux-gnu"
+
+define i64 @valid_strlen_i8_test1(ptr %Str) {
+; CHECK-LABEL: @valid_strlen_i8_test1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp eq ptr [[STR:%.*]], null
+; CHECK-NEXT:    br i1 [[TOBOOL]], label [[CLEANUP:%.*]], label [[LOR_LHS_FALSE:%.*]]
+; CHECK:       lor.lhs.false:
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[STR]], align 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label [[CLEANUP]], label [[FOR_INC_PREHEADER:%.*]]
+; CHECK:       for.inc.preheader:
+; CHECK-NEXT:    [[SCEVGEP:%.*]] = getelementptr i8, ptr [[STR]], i64 0
+; CHECK-NEXT:    [[STRLEN:%.*]] = call i64 @strlen(ptr [[SCEVGEP]])
+; CHECK-NEXT:    br label [[FOR_INC:%.*]]
+; CHECK:       for.inc:
+; CHECK-NEXT:    [[SRC_09:%.*]] = phi ptr [ poison, [[FOR_INC]] ], [ [[STR]], [[FOR_INC_PREHEADER]] ]
+; CHECK-NEXT:    [[TOBOOL2:%.*]] = icmp eq i8 poison, 0
+; CHECK-NEXT:    br i1 true, label [[FOR_END:%.*]], label [[FOR_INC]]
+; CHECK:       for.end:
+; CHECK-NEXT:    br label [[CLEANUP]]
+; CHECK:       cleanup:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i64 [ [[STRLEN]], [[FOR_END]] ], [ 0, [[ENTRY:%.*]] ], [ 0, [[LOR_LHS_FALSE]] ]
+; CHECK-NEXT:    ret i64 [[RETVAL_0]]
+;
+entry:
+  %tobool = icmp eq ptr %Str, null
+  br i1 %tobool, label %cleanup, label %lor.lhs.false
+
+lor.lhs.false:                                    ; preds = %entry
+  %0 = load i8, ptr %Str, align 1
+  %cmp = icmp eq i8 %0, 0
+  br i1 %cmp, label %cleanup, label %for.inc
+
+for.inc:                                          ; preds = %lor.lhs.false, %for.inc
+  %Src.09 = phi ptr [ %incdec.ptr, %for.inc ], [ %Str, %lor.lhs.false ]
+  %incdec.ptr = getelementptr inbounds i8, ptr %Src.09, i64 1
+  %.pr = load i8, ptr %incdec.ptr, align 1
+  %tobool2 = icmp eq i8 %.pr, 0
+  br i1 %tobool2, label %for.end, label %for.inc
+
+for.end:                                          ; preds = %for.inc
+  %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
+  br label %cleanup
+
+cleanup:                                          ; preds = %lor.lhs.false, %entry, %for.end
+  %retval.0 = phi i64 [ %sub.ptr.sub, %for.end ], [ 0, %entry ], [ 0, %lor.lhs.false ]
+  ret i64 %retval.0
+}
+
+define i64 @valid_strlen_i8_test2(ptr %Str) {
+; CHECK-LABEL: @valid_strlen_i8_test2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp eq ptr [[STR:%.*]], null
+; CHECK-NEXT:    br i1 [[TOBOOL]], label [[CLEANUP:%.*]], label [[FOR_COND_PREHEADER:%.*]]
+; CHECK:       for.cond.preheader:
+; CHECK-NEXT:    [[STRLEN:%.*]] = call i64 @strlen(ptr [[STR]])
+; CHECK-NEXT:    br label [[FOR_COND:%.*]]
+; CHECK:       for.cond:
+; CHECK-NEXT:    [[TOBOOL1:%.*]] = icmp eq i8 poison, 0
+; CHECK-NEXT:    [[INCDEC_PTR:%.*]] = getelementptr inbounds i8, ptr poison, i64 1
+; CHECK-NEXT:    br i1 true, label [[FOR_END:%.*]], label [[FOR_COND]]
+; CHECK:       for.end:
+; CHECK-NEXT:    br label [[CLEANUP]]
+; CHECK:       cleanup:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i64 [ [[STRLEN]], [[FOR_END]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    ret i64 [[RETVAL_0]]
+;
+entry:
+  %tobool = icmp eq ptr %Str, null
+  br i1 %tobool, label %cleanup, label %for.cond
+
+for.cond:                                         ; preds = %entry, %for.cond
+  %Src.0 = phi ptr [ %incdec.ptr, %for.cond ], [ %Str, %entry ]
+  %0 = load i8, ptr %Src.0, align 1
+  %tobool1 = icmp eq i8 %0, 0
+  %incdec.ptr = getelementptr inbounds i8, ptr %Src.0, i64 1
+  br i1 %tobool1, label %for.end, label %for.cond
+
+for.end:                                          ; preds = %for.cond
+  %sub.ptr.lhs.cast = ptrtoint ptr %Src.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
+  br label %cleanup
+
+  cleanup:                                          ; preds = %entry, %for.end
+  %retval.0 = phi i64 [ %sub.ptr.sub, %for.end ], [ 0, %entry ]
+  ret i64 %retval.0
+}
+
+define void @invalid_strlen_i8_test3(ptr %s, i32 zeroext %i) {
+; CHECK-LABEL: @invalid_strlen_i8_test3(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[WHILE_COND:%.*]]
+; CHECK:       while.cond:
+; CHECK-NEXT:    [[S_ADDR_0:%.*]] = phi ptr [ [[S:%.*]], [[ENTRY:%.*]] ], [ [[INCDEC_PTR1:%.*]], [[WHILE_COND]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[S_ADDR_0]], align 1
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq i8 [[TMP0]], 0
+; CHECK-NEXT:    [[INCDEC_PTR1]] = getelementptr inbounds i8, ptr [[S_ADDR_0]], i64 1
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label [[WHILE_END:%.*]], label [[WHILE_COND]]
+; CHECK:       while.end:
+; CHECK-NEXT:    [[S_ADDR_0_LCSSA:%.*]] = phi ptr [ [[S_ADDR_0]], [[WHILE_COND]] ]
+; CHECK-NEXT:    [[INCDEC_PTR1_LCSSA:%.*]] = phi ptr [ [[INCDEC_PTR1]], [[WHILE_COND]] ]
+; CHECK-NEXT:    store i8 45, ptr [[S_ADDR_0_LCSSA]], align 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[I:%.*]], 10
+; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    store i8 65, ptr [[INCDEC_PTR1_LCSSA]], align 1
+; CHECK-NEXT:    br label [[IF_END9:%.*]]
+; CHECK:       if.end:
+; CHECK-NEXT:    store i8 66, ptr [[INCDEC_PTR1_LCSSA]], align 1
+; CHECK-NEXT:    br label [[IF_END9]]
+; CHECK:       if.end9:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %while.cond
+
+while.cond:                                       ; preds = %while.cond, %entry
+  %s.addr.0 = phi ptr [ %s, %entry ], [ %incdec.ptr1, %while.cond ]
+  %0 = load i8, ptr %s.addr.0, align 1
+  %tobool.not = icmp eq i8 %0, 0
+  %incdec.ptr1 = getelementptr inbounds i8, ptr %s.addr.0, i64 1
+  br i1 %tobool.not, label %while.end, label %while.cond
+
+while.end:                                        ; preds = %while.cond
+  %s.addr.0.lcssa = phi ptr [ %s.addr.0, %while.cond ]
+  %incdec.ptr1.lcssa = phi ptr [ %incdec.ptr1, %while.cond ]
+  store i8 45, ptr %s.addr.0.lcssa, align 1
+  %cmp = icmp ult i32 %i, 10
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:                                          ; preds = %while.end
+  store i8 65, ptr %incdec.ptr1.lcssa, align 1
+  br label %if.end9
+
+if.end:                                           ; preds = %while.end
+  store i8 66, ptr %incdec.ptr1.lcssa, align 1
+  br label %if.end9
+
+if.end9:                                          ; preds = %if.end, %if.then
+  ret void
+}
+

>From 5335b7d53e172c3609f25064b370d1183eeb14d3 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry.jiang1 at ibm.com>
Date: Sun, 8 Sep 2024 13:17:03 -0400
Subject: [PATCH 2/2] enable strlen insert

---
 llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index 38cb79d7820c68..e5e25615750767 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -1499,7 +1499,7 @@ bool LoopIdiomRecognize::runOnNoncountableLoop() {
 
   return recognizePopcount() || recognizeAndInsertFFS() ||
          recognizeShiftUntilBitTest() || recognizeShiftUntilZero() ||
-         recognizeShiftUntilLessThan();
+         recognizeShiftUntilLessThan() || recognizeAndInsertStrLen();
 }
 
 /// Check if the given conditional branch is based on the comparison between



More information about the llvm-commits mailing list