[llvm] [Transforms] Recognize memcmp-like loops in LoopIdiomRecognize (PR #181562)

Sayan Sivakumaran via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 27 06:54:51 PDT 2026


https://github.com/sivakusayan updated https://github.com/llvm/llvm-project/pull/181562

>From e948ad7f39c2eefab886acbfe3bf9c52b8128bc5 Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Tue, 20 Jan 2026 17:37:02 -0600
Subject: [PATCH] [Transforms] LoopIdiomRecognize recognize memcmp

---
 .../Transforms/Scalar/LoopIdiomRecognize.h    |   3 +
 .../Transforms/Scalar/LoopIdiomRecognize.cpp  | 288 +++++++++++-
 llvm/test/Transforms/LoopIdiom/memcmp.ll      | 419 ++++++++++++++++++
 3 files changed, 698 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/Transforms/LoopIdiom/memcmp.ll

diff --git a/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h b/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h
index 109b4520878cb..ddd190bb68c2f 100644
--- a/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h
+++ b/llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h
@@ -43,6 +43,9 @@ struct DisableLIRP {
 
   /// When true, HashRecognize is disabled.
   static bool HashRecognize;
+
+  /// When true, Memcmp is disabled.
+  static bool Memcmp;
 };
 
 /// Performs Loop Idiom Recognize Pass.
diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index 105757140c758..98786fbc227ad 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -20,8 +20,6 @@
 //
 // TODO List:
 //
-// 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??).
 //
@@ -40,6 +38,7 @@
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/Analysis/CmpInstAnalysis.h"
 #include "llvm/Analysis/HashRecognize.h"
+#include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/LoopAccessAnalysis.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/LoopPass.h"
@@ -53,6 +52,7 @@
 #include "llvm/Analysis/ScalarEvolutionPatternMatch.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Analysis/Utils/Local.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constant.h"
@@ -100,6 +100,7 @@ using namespace SCEVPatternMatch;
 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(NumMemCmp, "Number of uncountable loops recognized as memcmp idiom");
 STATISTIC(NumStrLen, "Number of strlen's and wcslen's formed from loop loads");
 STATISTIC(
     NumShiftUntilBitTest,
@@ -131,6 +132,14 @@ static cl::opt<bool, true>
                       cl::location(DisableLIRP::Memcpy), cl::init(false),
                       cl::ReallyHidden);
 
+bool DisableLIRP::Memcmp;
+static cl::opt<bool, true>
+    DisableLIRPMemcmp("disable-loop-idiom-memcmp",
+                      cl::desc("Proceed with loop idiom recognize pass, but do "
+                               "not convert loop(s) to memcmp."),
+                      cl::location(DisableLIRP::Memcmp), cl::init(false),
+                      cl::ReallyHidden);
+
 bool DisableLIRP::Strlen;
 static cl::opt<bool, true>
     DisableLIRPStrlen("disable-loop-idiom-strlen",
@@ -182,17 +191,17 @@ class LoopIdiomRecognize {
   const TargetTransformInfo *TTI;
   const DataLayout *DL;
   OptimizationRemarkEmitter &ORE;
+  AssumptionCache *AC;
   bool ApplyCodeSizeHeuristics;
   std::unique_ptr<MemorySSAUpdater> MSSAU;
 
 public:
-  explicit LoopIdiomRecognize(AliasAnalysis *AA, DominatorTree *DT,
-                              LoopInfo *LI, ScalarEvolution *SE,
-                              TargetLibraryInfo *TLI,
-                              const TargetTransformInfo *TTI, MemorySSA *MSSA,
-                              const DataLayout *DL,
-                              OptimizationRemarkEmitter &ORE)
-      : AA(AA), DT(DT), LI(LI), SE(SE), TLI(TLI), TTI(TTI), DL(DL), ORE(ORE) {
+  explicit LoopIdiomRecognize(
+      AliasAnalysis *AA, DominatorTree *DT, LoopInfo *LI, ScalarEvolution *SE,
+      TargetLibraryInfo *TLI, const TargetTransformInfo *TTI, MemorySSA *MSSA,
+      const DataLayout *DL, OptimizationRemarkEmitter &ORE, AssumptionCache *AC)
+      : AA(AA), DT(DT), LI(LI), SE(SE), TLI(TLI), TTI(TTI), DL(DL), ORE(ORE),
+        AC(AC) {
     if (MSSA)
       MSSAU = std::make_unique<MemorySSAUpdater>(MSSA);
   }
@@ -286,6 +295,7 @@ class LoopIdiomRecognize {
   bool recognizeShiftUntilBitTest();
   bool recognizeShiftUntilZero();
   bool recognizeAndInsertStrLen();
+  bool recognizeAndInsertMemcmp();
 
   /// @}
 };
@@ -305,7 +315,7 @@ PreservedAnalyses LoopIdiomRecognizePass::run(Loop &L, LoopAnalysisManager &AM,
   OptimizationRemarkEmitter ORE(L.getHeader()->getParent());
 
   LoopIdiomRecognize LIR(&AR.AA, &AR.DT, &AR.LI, &AR.SE, &AR.TLI, &AR.TTI,
-                         AR.MSSA, DL, ORE);
+                         AR.MSSA, DL, ORE, &AR.AC);
   if (!LIR.runOnLoop(&L))
     return PreservedAnalyses::all();
 
@@ -336,7 +346,7 @@ bool LoopIdiomRecognize::runOnLoop(Loop *L) {
   // Disable loop idiom recognition if the function's name is a common idiom.
   StringRef Name = L->getHeader()->getParent()->getName();
   if (Name == "memset" || Name == "memcpy" || Name == "strlen" ||
-      Name == "wcslen")
+      Name == "wcslen" || Name == "memcmp")
     return false;
 
   // Determine if code size heuristics need to be applied.
@@ -1698,7 +1708,8 @@ bool LoopIdiomRecognize::runOnNoncountableLoop() {
 
   return recognizePopcount() || recognizeAndInsertFFS() ||
          recognizeShiftUntilBitTest() || recognizeShiftUntilZero() ||
-         recognizeShiftUntilLessThan() || recognizeAndInsertStrLen();
+         recognizeShiftUntilLessThan() || recognizeAndInsertStrLen() ||
+         recognizeAndInsertMemcmp();
 }
 
 /// Check if the given conditional branch is based on the comparison between
@@ -3577,3 +3588,256 @@ bool LoopIdiomRecognize::recognizeShiftUntilZero() {
   ++NumShiftUntilZero;
   return MadeChange;
 }
+
+namespace {
+class MemcmpVerifier {
+public:
+  explicit MemcmpVerifier(Loop *CurLoop, ScalarEvolution *SE, DominatorTree *DT,
+                          AssumptionCache *AC, const DataLayout *DL)
+      : CurLoop(CurLoop), SE(SE), DT(DT), AC(AC), DL(DL) {}
+
+  enum class MemcmpCandidateResult {
+    RejectNotSimple,
+    RejectLoadingNonInteger,
+    RejectLoadingIntegersWithPadding,
+    RejectUnexpectedSCEV,
+    RejectPossibleOutOfBounds,
+    RejectUnexpectedAddressSpace,
+    Accept
+  };
+
+  MemcmpCandidateResult isLoadMemcmpCandidate(LoadInst *LI,
+                                              const SCEVUnknown *&Base,
+                                              const APInt *&Step) {
+    if (!LI->isSimple())
+      return MemcmpCandidateResult::RejectNotSimple;
+
+    Value *LoadPointer = LI->getPointerOperand();
+    if (LoadPointer->getType()->getPointerAddressSpace() != 0)
+      return MemcmpCandidateResult::RejectUnexpectedAddressSpace;
+
+    // Comparisons of floats can't be transformed. For example, the bits of
+    // two NaN values might be equivalent, but NaN is never equal to itself.
+    // This means `memcmp` would be a behavior change from float equality.
+    IntegerType *LoadType = dyn_cast<IntegerType>(LI->getType());
+    if (!LoadType)
+      return MemcmpCandidateResult::RejectLoadingNonInteger;
+
+    // There should be no padding between consecutive members of the integer
+    // array, as `memcmp` could give a different answer from integer equality.
+    if (DL->getTypeAllocSizeInBits(LoadType) != DL->getTypeSizeInBits(LoadType))
+      return MemcmpCandidateResult::RejectLoadingIntegersWithPadding;
+
+    // If the Load SCEV has a non-constant step, it is clearly not part of a
+    // `memcmp` idiom. Furthermore, the step must be equal to the size of
+    // the load type, as otherwise the for loop would skip equality checks
+    // for certain bytes.
+    const SCEVUse LoadSCEV = SE->getSCEVAtScope(LoadPointer, CurLoop);
+    const SCEVUnknown *LoadBase;
+    const APInt *LoadStep;
+    if (!match(LoadSCEV, m_scev_AffineAddRec(m_SCEVUnknown(LoadBase),
+                                             m_scev_APInt(LoadStep)))) {
+      return MemcmpCandidateResult::RejectUnexpectedSCEV;
+    }
+    if (LoadType->getBitWidth() != *LoadStep * CHAR_BIT)
+      return MemcmpCandidateResult::RejectUnexpectedSCEV;
+
+    // TODO: Possibly handle negative strides.
+    if (LoadStep->isNegative())
+      return MemcmpCandidateResult::RejectUnexpectedSCEV;
+
+    // If it's possible for the load to go out of bounds, then this certainly
+    // can't be transformed. The buffers passed into memcmp must both be at
+    // least as large as the size argument passed into it.
+    if (!llvm::isDereferenceableAndAlignedInLoop(LI, CurLoop, *SE, *DT, AC))
+      return MemcmpCandidateResult::RejectPossibleOutOfBounds;
+
+    Base = LoadBase;
+    Step = LoadStep;
+    assert(Base);
+    assert(Step);
+    assert(SE->isLoopInvariant(Base, CurLoop) &&
+           "A valid memcmp operand should not be loop variant");
+    return MemcmpCandidateResult::Accept;
+  }
+
+  /// We are trying to detect the following memcmp-like structure:
+  ///
+  /// preheader:
+  ///   ...
+  ///   br label %body
+  ///
+  /// body:
+  ///   ... ; Both loads have equal SCEV steps and satisfy certain properties
+  ///   %lhs = load i32, ptr %lhs_ptr
+  ///   %rhs = load i32, ptr %rhs_ptr
+  ///   %equal = icmp eq i32 %lhs, %rhs
+  ///   br i1 %equal, label %cond, label %exit
+  ///
+  /// cond:
+  ///   ... ; Compute whether loop should stop using some induction variable
+  ///   br i1 %stop_loop, label %exit, label %body
+  ///
+  /// exit:
+  ///   %buffers_equal = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ///
+  /// More specifically, we expect the pairs of loads to have a SCEV expression
+  /// of the form {%buffer,+,c}, where c is a ConstantInt that is equal to the
+  /// bitwidth of the load type. Furthermore, we must be able to prove that
+  /// there are no padding bytes in the buffers being read from, and that the
+  /// loads are never out of bounds up to the maximum number of times the
+  /// loop backedge is taken.
+  bool detectMemcmpIdiom(PHINode *&LCSSAPhiToReplace, Value *&LHS, Value *&RHS,
+                         const SCEV *&LenSCEV) {
+    // Step 1: Make sure we have single header, latch and exit block.
+    // We expect this loop to be rotated, so the loop header will be
+    // the body block described above.
+    BasicBlock *BodyBlock = CurLoop->getHeader();
+    BasicBlock *CondBlock = CurLoop->getLoopLatch();
+    BasicBlock *ExitBlock = CurLoop->getUniqueExitBlock();
+    if (!BodyBlock || !CondBlock || !ExitBlock)
+      return false;
+
+    for (const Instruction &I : *BodyBlock)
+      if (I.mayHaveSideEffects())
+        return false;
+
+    for (const Instruction &I : *CondBlock)
+      if (I.mayHaveSideEffects())
+        return false;
+
+    // Step 2: The pattern we are looking for has precisely one LCSSAPhi.
+    // Furthermore, that LCSSAPhi is constant.
+    if (std::distance(ExitBlock->phis().begin(), ExitBlock->phis().end()) != 1)
+      return false;
+
+    PHINode &Phi = *ExitBlock->phis().begin();
+    Value *Val = Phi.hasConstantValue();
+    if (!Val)
+      return false;
+
+    // Step 3: Verify that the value being used in the LCSSAPhi is actually
+    // a compare operation, and that the branch operation looks correct.
+    using namespace PatternMatch;
+    const auto *CI = dyn_cast<CmpInst>(Val);
+    if (!CI)
+      return false;
+
+    const auto *BodyTerminator =
+        dyn_cast<CondBrInst>(BodyBlock->getTerminator());
+    if (!match(BodyTerminator, m_Br(m_Specific(CI), m_SpecificBB(CondBlock),
+                                    m_SpecificBB(ExitBlock)))) {
+      return false;
+    }
+
+    // Step 4: Verify that the compare operation is comparing two
+    // pointers satisfying certain criteria. See
+    // `isLoadMemcmpCandidate()` for more information.
+    auto *LoadLHS = dyn_cast<LoadInst>(CI->getOperand(0));
+    auto *LoadRHS = dyn_cast<LoadInst>(CI->getOperand(1));
+    if (!LoadLHS || !LoadRHS) {
+      return false;
+    }
+
+    const SCEVUnknown *BaseLHS;
+    const SCEVUnknown *BaseRHS;
+    const APInt *StepLHS;
+    const APInt *StepRHS;
+    if (isLoadMemcmpCandidate(LoadLHS, BaseLHS, StepLHS) !=
+        MemcmpCandidateResult::Accept) {
+      LLVM_DEBUG(dbgs() << "  LHS loads fail memcmp idiom\n");
+      return false;
+    }
+    if (isLoadMemcmpCandidate(LoadRHS, BaseRHS, StepRHS) !=
+        MemcmpCandidateResult::Accept) {
+      LLVM_DEBUG(dbgs() << "  RHS loads fail memcmp idiom\n");
+      return false;
+    }
+    if (*StepLHS != *StepRHS)
+      return false;
+
+    // Step 5: Get a backedge count that we can use to construct
+    // the length arg that will be passed into the emitted
+    // memcmp call.
+    const SCEV *MaxBackedgeTaken =
+        llvm::dyn_cast<SCEVConstant>(SE->getBackedgeTakenCount(
+            CurLoop, llvm::ScalarEvolution::ConstantMaximum));
+    if (llvm::isa<SCEVCouldNotCompute>(MaxBackedgeTaken))
+      return false;
+
+    // Step 6: Celebrate! This transform is safe to be done, so return
+    // the information needed for the caller to emit an equivalent
+    // memcmp.
+    LCSSAPhiToReplace = Φ
+    LHS = BaseLHS->getValue();
+    RHS = BaseRHS->getValue();
+    Type *IndexTy = DL->getIndexType(LHS->getType());
+    LenSCEV = getNumBytes(MaxBackedgeTaken, IndexTy, SE->getConstant(*StepLHS),
+                          CurLoop, DL, SE);
+
+    assert(LHS);
+    assert(RHS);
+    assert(LenSCEV);
+    assert(SE->isLoopInvariant(LenSCEV, CurLoop));
+    return true;
+  }
+
+private:
+  Loop *CurLoop;
+  ScalarEvolution *SE;
+  DominatorTree *DT;
+  AssumptionCache *AC;
+  const DataLayout *DL;
+};
+} // namespace
+
+bool LoopIdiomRecognize::recognizeAndInsertMemcmp() {
+  if (!TLI->has(LibFunc_memcmp) || DisableLIRPMemcmp)
+    return false;
+
+  // Our goal is to find a singular LCSSAPhi that is using a byte by byte
+  // equality check of two buffers. See `detectMemcmpIdiom` for more details.
+  PHINode *LCSSAPhi;
+  Value *LHS;
+  Value *RHS;
+  const SCEV *LenSCEV;
+  MemcmpVerifier Verifier(CurLoop, SE, DT, AC, DL);
+  if (!Verifier.detectMemcmpIdiom(LCSSAPhi, LHS, RHS, LenSCEV))
+    return false;
+
+  // Okay, it's time to emit the memcmp call. Since we proved that the loop's
+  // LCSSAPhi is checking the byte equality of the buffers, we'll replace it
+  // with the sequence:
+  //
+  // %result = call @memcmp(ptr %LHS, ptr %RHS, %ExpandedLenScev)
+  // %ret = icmp eq i32 %result, 0
+  //
+  // Emitting in the exit block should be sufficient, as the LHS and RHS will
+  // certainly dominate their usages in the exit.
+  IRBuilder<> Builder(LCSSAPhi);
+  SCEVExpander Expander(*SE, "loop-idiom");
+  SCEVExpanderCleaner ExpCleaner(Expander);
+  assert(Expander.isSafeToExpand(LenSCEV) && "SCEV should be trivial");
+
+  Value *LenArg = Expander.expandCodeFor(LenSCEV, LenSCEV->getType(), LCSSAPhi);
+  Value *MemCmpCall = llvm::emitMemCmp(LHS, RHS, LenArg, Builder, *DL, TLI);
+  Value *NewCmpInst = Builder.CreateCmp(
+      CmpInst::ICMP_EQ, MemCmpCall,
+      llvm::ConstantInt::get(
+          Builder.getContext(),
+          llvm::APInt(MemCmpCall->getType()->getPrimitiveSizeInBits(), 0)));
+  LCSSAPhi->replaceAllUsesWith(NewCmpInst);
+  RecursivelyDeleteDeadPHINode(LCSSAPhi);
+
+  // Okay, our work should be done. We'll let the loop-deletion pass
+  // handle cleaning up the dead loop.
+  ++NumMemCmp;
+  LLVM_DEBUG(dbgs() << "Formed memcmp idiom:" << *MemCmpCall << "\n");
+  ORE.emit([&]() {
+    return OptimizationRemark(DEBUG_TYPE, "recognizeAndInsertMemcmp",
+                              CurLoop->getStartLoc(), LCSSAPhi->getParent())
+           << "Transformed memcmp loop idiom";
+  });
+  ExpCleaner.markResultUsed();
+  return true;
+}
diff --git a/llvm/test/Transforms/LoopIdiom/memcmp.ll b/llvm/test/Transforms/LoopIdiom/memcmp.ll
new file mode 100644
index 0000000000000..a103aae1cdd5c
--- /dev/null
+++ b/llvm/test/Transforms/LoopIdiom/memcmp.ll
@@ -0,0 +1,419 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s
+
+ at some_global = external global i8
+%arr = type { [50 x i8] }
+%arr_i16 = type { [50 x i16] }
+%arr_too_short = type { [49 x i8] }
+%arr_elements_have_padding = type { [50 x i36] }
+%arr_floats = type { [50 x float] }
+
+define i1 @memcmp_idiom(ptr byval(%arr) align 8 %lhs, ptr byval(%arr) align 8 %rhs) {
+; CHECK-LABEL: define i1 @memcmp_idiom(
+; CHECK-SAME: ptr byval([[ARR:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i8, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i8, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i8 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[MEMCMP:%.*]] = call i32 @memcmp(ptr [[LHS]], ptr [[RHS]], i64 50)
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i32 [[MEMCMP]], 0
+; CHECK-NEXT:    ret i1 [[TMP0]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load i8, ptr %lhs_addr, align 1
+  %rhs_val = load i8, ptr %rhs_addr, align 1
+  %equal = icmp eq i8 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @memcmp_idiom_recognized_with_nonchar_bitwidth(ptr byval(%arr_i16) align 8 %lhs, ptr byval(%arr_i16) align 8 %rhs) {
+; CHECK-LABEL: define i1 @memcmp_idiom_recognized_with_nonchar_bitwidth(
+; CHECK-SAME: ptr byval([[ARR_I16:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR_I16]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i16, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i16, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i16, ptr [[LHS_ADDR]], align 2
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i16, ptr [[RHS_ADDR]], align 2
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i16 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[MEMCMP:%.*]] = call i32 @memcmp(ptr [[LHS]], ptr [[RHS]], i64 100)
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i32 [[MEMCMP]], 0
+; CHECK-NEXT:    ret i1 [[TMP0]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %lhs_addr = getelementptr inbounds nuw i16, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i16, ptr %rhs, i64 %indvar
+  %lhs_val = load i16, ptr %lhs_addr, align 2
+  %rhs_val = load i16, ptr %rhs_addr, align 2
+  %equal = icmp eq i16 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_potential_out_of_bounds_read(ptr byval(%arr_too_short) align 8 %lhs, ptr byval(%arr_too_short) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_potential_out_of_bounds_read(
+; CHECK-SAME: ptr byval([[ARR_TOO_SHORT:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR_TOO_SHORT]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i8, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i8, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i8 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RET:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load i8, ptr %lhs_addr, align 1
+  %rhs_val = load i8, ptr %rhs_addr, align 1
+  %equal = icmp eq i8 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_has_padding(ptr byval(%arr_elements_have_padding) align 8 %lhs, ptr byval(%arr_elements_have_padding) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_has_padding(
+; CHECK-SAME: ptr byval([[ARR_ELEMENTS_HAVE_PADDING:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR_ELEMENTS_HAVE_PADDING]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i36, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i36, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i36 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RET:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load i36, ptr %lhs_addr, align 1
+  %rhs_val = load i36, ptr %rhs_addr, align 1
+  %equal = icmp eq i36 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_mismatched_stride(ptr byval(%arr) align 8 %lhs, ptr byval(%arr) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_mismatched_stride(
+; CHECK-SAME: ptr byval([[ARR:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 25
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[DOUBLEINDVAR:%.*]] = mul i64 [[INDVAR]], 2
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[DOUBLEINDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i8, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i8, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i8 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[TMP0]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 25
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %doubleindvar = mul i64 %indvar, 2
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %doubleindvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load i8, ptr %lhs_addr, align 1
+  %rhs_val = load i8, ptr %rhs_addr, align 1
+  %equal = icmp eq i8 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_loads_skip_bytes(ptr byval(%arr) align 8 %lhs, ptr byval(%arr) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_loads_skip_bytes(
+; CHECK-SAME: ptr byval([[ARR:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 25
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[DOUBLEINDVAR:%.*]] = mul i64 [[INDVAR]], 2
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[DOUBLEINDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[DOUBLEINDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i8, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i8, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i8 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RET:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 25
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %doubleindvar = mul i64 %indvar, 2
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %doubleindvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %doubleindvar
+  %lhs_val = load i8, ptr %lhs_addr, align 1
+  %rhs_val = load i8, ptr %rhs_addr, align 1
+  %equal = icmp eq i8 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_volatile_loads(ptr byval(%arr) align 8 %lhs, ptr byval(%arr) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_volatile_loads(
+; CHECK-SAME: ptr byval([[ARR:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load volatile i8, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load volatile i8, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i8 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[TMP0]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load volatile i8, ptr %lhs_addr, align 1
+  %rhs_val = load volatile i8, ptr %rhs_addr, align 1
+  %equal = icmp eq i8 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_float_comparisons(ptr byval(%arr_floats) align 8 %lhs, ptr byval(%arr_floats) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_float_comparisons(
+; CHECK-SAME: ptr byval([[ARR_FLOATS:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR_FLOATS]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load float, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load float, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = fcmp oeq float [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[TMP0]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load float, ptr %lhs_addr, align 1
+  %rhs_val = load float, ptr %rhs_addr, align 1
+  %equal = fcmp oeq float %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_cond_block_side_effect(ptr byval(%arr) align 8 %lhs, ptr byval(%arr) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_cond_block_side_effect(
+; CHECK-SAME: ptr byval([[ARR:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    store i8 0, ptr [[LHS]], align 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i8, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i8, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i8 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RET:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  store i8 0, ptr %lhs
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load i8, ptr %lhs_addr, align 1
+  %rhs_val = load i8, ptr %rhs_addr, align 1
+  %equal = icmp eq i8 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}
+
+define i1 @no_idiom_body_block_side_effect(ptr byval(%arr) align 8 %lhs, ptr byval(%arr) align 8 %rhs) {
+; CHECK-LABEL: define i1 @no_idiom_body_block_side_effect(
+; CHECK-SAME: ptr byval([[ARR:%.*]]) align 8 [[LHS:%.*]], ptr byval([[ARR]]) align 8 [[RHS:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[BODY:.*]]
+; CHECK:       [[COND:.*]]:
+; CHECK-NEXT:    [[INC:%.*]] = add nuw nsw i64 [[INDVAR:%.*]], 1
+; CHECK-NEXT:    [[STOP_LOOP:%.*]] = icmp eq i64 [[INC]], 50
+; CHECK-NEXT:    br i1 [[STOP_LOOP]], label %[[EXIT:.*]], label %[[BODY]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVAR]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INC]], %[[COND]] ]
+; CHECK-NEXT:    store i8 0, ptr [[LHS]], align 1
+; CHECK-NEXT:    [[LHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[LHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[RHS_ADDR:%.*]] = getelementptr inbounds nuw i8, ptr [[RHS]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[LHS_VAL:%.*]] = load i8, ptr [[LHS_ADDR]], align 1
+; CHECK-NEXT:    [[RHS_VAL:%.*]] = load i8, ptr [[RHS_ADDR]], align 1
+; CHECK-NEXT:    [[EQUAL:%.*]] = icmp eq i8 [[LHS_VAL]], [[RHS_VAL]]
+; CHECK-NEXT:    br i1 [[EQUAL]], label %[[COND]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RET:%.*]] = phi i1 [ [[EQUAL]], %[[BODY]] ], [ [[EQUAL]], %[[COND]] ]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+entry:
+  br label %body
+cond:
+  %inc = add nuw nsw i64 %indvar, 1
+  %stop_loop = icmp eq i64 %inc, 50
+  br i1 %stop_loop, label %exit, label %body
+body:
+  %indvar = phi i64 [ 0, %entry ], [ %inc, %cond ]
+  store i8 0, ptr %lhs
+  %lhs_addr = getelementptr inbounds nuw i8, ptr %lhs, i64 %indvar
+  %rhs_addr = getelementptr inbounds nuw i8, ptr %rhs, i64 %indvar
+  %lhs_val = load i8, ptr %lhs_addr, align 1
+  %rhs_val = load i8, ptr %rhs_addr, align 1
+  %equal = icmp eq i8 %lhs_val, %rhs_val
+  br i1 %equal, label %cond, label %exit
+exit:
+  %ret = phi i1 [ %equal, %body ], [ %equal, %cond ]
+  ret i1 %ret
+}



More information about the llvm-commits mailing list