[llvm] [GVN] Support rnflow pattern matching and transform (PR #162259)

Madhur Amilkanthwar via llvm-commits llvm-commits at lists.llvm.org
Mon May 18 06:23:34 PDT 2026


https://github.com/madhur13490 updated https://github.com/llvm/llvm-project/pull/162259

>From 568f755365c514a8c3d7eb176389d61e9866362f Mon Sep 17 00:00:00 2001
From: Madhur Amilkanthwar <madhura at nvidia.com>
Date: Mon, 18 May 2026 06:20:48 -0700
Subject: [PATCH] [GVN] Support rnflow pattern matching and transform

---
 llvm/include/llvm/Transforms/Scalar/GVN.h     |    9 +
 llvm/lib/Transforms/Scalar/GVN.cpp            |  434 +++++++
 .../Transforms/GVN/PRE/gvn-min-pre-basic.ll   |  236 ++++
 .../GVN/PRE/gvn-min-pre-negatives.ll          | 1007 +++++++++++++++++
 .../GVN/PRE/gvn-min-pre-predicates.ll         |  304 +++++
 5 files changed, 1990 insertions(+)
 create mode 100644 llvm/test/Transforms/GVN/PRE/gvn-min-pre-basic.ll
 create mode 100644 llvm/test/Transforms/GVN/PRE/gvn-min-pre-negatives.ll
 create mode 100644 llvm/test/Transforms/GVN/PRE/gvn-min-pre-predicates.ll

diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h
index 806e969211e7a..062d4893ebee4 100644
--- a/llvm/include/llvm/Transforms/Scalar/GVN.h
+++ b/llvm/include/llvm/Transforms/Scalar/GVN.h
@@ -45,6 +45,8 @@ class FunctionPass;
 class GetElementPtrInst;
 class ImplicitControlFlowTracking;
 class LoadInst;
+class Loop;
+class SelectInst;
 class LoopInfo;
 class MemDepResult;
 class MemoryAccess;
@@ -421,6 +423,13 @@ class GVNPass : public OptionalPassInfoMixin<GVNPass> {
   void addDeadBlock(BasicBlock *BB);
   void assignValNumForDeadCode();
   void assignBlockRPONumber(Function &F);
+
+  bool recognizeMinFindingSelectPattern(SelectInst *Select);
+  bool transformMinFindingSelectPattern(Loop *L, Type *LoadType,
+                                        BasicBlock *Preheader, BasicBlock *BB,
+                                        CmpInst *Comparison, SelectInst *Select,
+                                        Value *BasePtr, PHINode *IndexValPhi,
+                                        Value *OffsetVal);
 };
 
 /// Create a legacy GVN pass.
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index c069c1600a79b..0fa18e86b2bab 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -72,6 +72,7 @@
 #include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Transforms/Utils/Local.h"
+#include "llvm/Transforms/Utils/LoopUtils.h"
 #include "llvm/Transforms/Utils/SSAUpdater.h"
 #include "llvm/Transforms/Utils/VNCoercion.h"
 #include <algorithm>
@@ -138,6 +139,10 @@ static cl::opt<uint32_t> MaxNumInsnsPerBlock(
     cl::desc("Max number of instructions to scan in each basic block in GVN "
              "(default = 100)"));
 
+static cl::opt<bool> GVNEnableMinFindingSelectPattern(
+    "gvn-enable-min-finding-select-pattern", cl::init(false), cl::Hidden,
+    cl::desc("Enable GVN transformation for minimum finding select pattern"));
+
 struct llvm::GVNPass::Expression {
   uint32_t Opcode;
   bool Commutative = false;
@@ -2747,6 +2752,11 @@ bool GVNPass::processInstruction(Instruction *I) {
     }
     return Changed;
   }
+  if (SelectInst *Select = dyn_cast<SelectInst>(I)) {
+    if (GVNEnableMinFindingSelectPattern &&
+        recognizeMinFindingSelectPattern(Select))
+      return true;
+  }
 
   // Instructions with void type don't return a value, so there's
   // no point in trying to find redundancies in them.
@@ -3333,6 +3343,430 @@ void GVNPass::assignValNumForDeadCode() {
   }
 }
 
+/// Return true if no instruction in the loop may write the load's
+/// memory location after the load executes. The transform memoizes the
+/// load's value across iterations, so any post-load mod in any loop
+/// block becomes a backedge clobber that invalidates the memoization.
+/// Scan every loop block; on the load's own block start from the next
+/// instruction, otherwise from the block's start.
+static bool canHoistLoadWithAA(Loop *L, LoadInst *Load, AAResults *AA) {
+  MemoryLocation Loc = MemoryLocation::get(Load);
+  for (BasicBlock *BB : L->blocks()) {
+    BasicBlock::iterator It =
+        BB == Load->getParent() ? std::next(Load->getIterator()) : BB->begin();
+    for (auto E = BB->end(); It != E; ++It) {
+      Instruction &I = *It;
+      if (!I.mayWriteToMemory())
+        continue;
+      if (isModSet(AA->getModRefInfo(&I, Loc))) {
+        LLVM_DEBUG(dbgs() << "GVN (minindx): Cannot hoist - clobbered in "
+                             "loop by "
+                          << I << "\n");
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+/// Hoist the chain of operations for the second load to preheader.
+/// In this transformation, we hoist the redundant load to the preheader,
+/// caching the first value of the iteration. This value is used to compare with
+/// the current value of the iteration and update the minimum value.
+/// The comparison is done in the loop body using the new select instruction.
+///
+/// *** Before transformation ***
+///
+///  preheader:
+///    ...
+///  loop:
+///    ...
+///    ...
+///    %val.first = load <TYPE>, ptr %ptr.first.load, align 4
+///    %min.idx.ext = sext i32 %min.idx to i64
+///    %ptr.<TYPE>.min = getelementptr <TYPE>, ptr %0, i64 %min.idx.ext
+///    %ptr.second.load = getelementptr i8, ptr %ptr.<TYPE>.min, i64 -4
+///    %val.current.min = load <TYPE>, ptr %ptr.second.load, align 4
+///    ...
+///    ...
+///    br i1 %cond, label %loop, label %exit
+///
+///    We capture <TYPE> as a part of pattern matching and then later
+///    use it in the transformation.
+///
+/// *** After transformation ***
+///
+///  preheader:
+///    %min.idx.ext = sext i32 %min.idx.ext to i64
+///    %hoist_gep1 = getelementptr <TYPE>, ptr %0, i64 %min.idx.ext
+///    %hoist_gep2 = getelementptr i8, ptr %hoist_gep1, i64 -4
+///    %hoisted_load = load <TYPE>, ptr %hoist_gep2, align 4
+///    br label %loop
+///
+///  loop:
+///    %val.first = load <TYPE>, ptr %ptr.first.load, align 4
+///    ...
+///    (new) %val.current.min = select i1 %cond, <TYPE> %hoisted_load, <TYPE>
+///    %val.current.min
+///    ...
+///    ...
+///    br i1 %cond, label %loop, label %exit
+bool GVNPass::transformMinFindingSelectPattern(
+    Loop *L, Type *LoadType, BasicBlock *Preheader, BasicBlock *BB,
+    CmpInst *Comparison, SelectInst *Select, Value *BasePtr,
+    PHINode *IndexValPhi, Value *OffsetVal) {
+
+  assert(BasePtr && "BasePtr is null");
+  assert(OffsetVal && "OffsetVal is null");
+  assert(IndexValPhi && "IndexValPhi is null");
+
+  // The recognizer canonicalizes the matched (hoistable) load to operand(1)
+  // of the compare. Capture both operands now, before we mutate the compare
+  // below.
+  Value *LHS = Comparison->getOperand(0);
+  Value *LoadVal = Comparison->getOperand(1);
+
+  // The transform memoizes the hoistable load's value across iterations, so
+  // any in-loop store or call that may write the load's location is a
+  // backedge clobber that invalidates the cache. With MSSA, use sink-mode
+  // invalidation (walks every MemoryDef in the loop with phi-translation).
+  // Without MSSA, walk the loop's instructions with AA.
+  auto *HoistableLoad = cast<LoadInst>(LoadVal);
+  bool CanHoist;
+  if (MSSAU) {
+    MemorySSA &MSSA = *MSSAU->getMemorySSA();
+    SinkAndHoistLICMFlags Flags(/*IsSink=*/true, *L, MSSA);
+    CanHoist = canHoistLoad(*HoistableLoad, VN.getAliasAnalysis(), DT, L, MSSA,
+                            /*TargetExecutesOncePerLoop=*/true, Flags, ORE);
+  } else {
+    CanHoist = canHoistLoadWithAA(L, HoistableLoad, VN.getAliasAnalysis());
+  }
+  if (!CanHoist) {
+    LLVM_DEBUG(dbgs() << "GVN: Cannot hoist - may be clobbered by some "
+                         "instruction in the loop.\n");
+    return false;
+  }
+
+  IRBuilder<> Builder(Preheader->getTerminator());
+  Value *InitialMinIndex = IndexValPhi->getIncomingValueForBlock(Preheader);
+
+  // Insert PHI node at the top of this block.
+  // This PHI node will be used to memoize the current minimum value so far.
+  PHINode *KnownMinPhi = PHINode::Create(LoadType, 2, "known_min", BB->begin());
+
+  // Hoist the load and build the necessary operations.
+  // 1. hoist_0 = sext i32 1 to i64
+  Value *HoistedSExt =
+      Builder.CreateSExt(InitialMinIndex, Builder.getInt64Ty(), "hoist_sext");
+
+  // 2. hoist_gep1 = getelementptr float, ptr BasePtr, i64 HoistedSExt
+  Value *HoistedGEP1 =
+      Builder.CreateGEP(LoadType, BasePtr, HoistedSExt, "hoist_gep1");
+
+  // 3. hoist_gep2 = getelementptr i8, ptr HoistedGEP1, i64 OffsetVal
+  Value *HoistedGEP2 = Builder.CreateGEP(Builder.getInt8Ty(), HoistedGEP1,
+                                         OffsetVal, "hoist_gep2");
+
+  // 4. hoisted_load = load float, ptr HoistedGEP2
+  // Preserve alignment and metadata from the original load; IRBuilder's
+  // natural alignment may exceed what the user declared, which would
+  // fault on strict-alignment targets at runtime.
+  LoadInst *OrigLoad = cast<LoadInst>(LoadVal);
+  LoadInst *NewLoad = Builder.CreateLoad(LoadType, HoistedGEP2, "hoisted_load");
+  NewLoad->setAlignment(OrigLoad->getAlign());
+  copyMetadataForLoad(*NewLoad, *OrigLoad);
+
+  // Update MemorySSA before erasing the original load.
+  if (MSSAU) {
+    auto *OrigUse =
+        MSSAU->getMemorySSA()->getMemoryAccess(dyn_cast<Instruction>(LoadVal));
+    if (OrigUse) {
+      MemoryAccess *DefiningAccess = OrigUse->getDefiningAccess();
+      MSSAU->createMemoryAccessInBB(NewLoad, DefiningAccess, Preheader,
+                                    MemorySSA::BeforeTerminator);
+      MSSAU->removeMemoryAccess(OrigUse);
+    }
+  }
+
+  // Invalidate MD cache for the loaded pointer; we added a new load and removed
+  // the old one (removeInstruction handles removing the old load from MD).
+  if (MD)
+    MD->invalidateCachedPointerInfo(NewLoad->getPointerOperand());
+
+  // Let the new load now take the place of the old load.
+  LoadVal->replaceAllUsesWith(NewLoad);
+  Instruction *LoadInst = dyn_cast<Instruction>(LoadVal);
+  if (uint32_t ValNo = VN.lookup(LoadInst, false))
+    LeaderTable.erase(ValNo, LoadInst, LoadInst->getParent());
+  removeInstruction(LoadInst);
+
+  // Comparison should now compare the current value and the newly inserted
+  // PHI node.
+  Comparison->setOperand(1, KnownMinPhi);
+
+  // Create new select instruction for selecting the minimum value.
+  IRBuilder<> SelectBuilder(BB->getTerminator());
+  SelectInst *CurrentMinSelect = dyn_cast<SelectInst>(
+      SelectBuilder.CreateSelect(Comparison, LHS, KnownMinPhi, "current_min"));
+
+  // Populate the newly created PHI node
+  // with (hoisted) NewLoad from the preheader and CurrentMinSelect.
+  KnownMinPhi->addIncoming(NewLoad, Preheader);
+  KnownMinPhi->addIncoming(CurrentMinSelect, BB);
+  LLVM_DEBUG(
+      dbgs() << "GVN: Transformed the code for minimum finding pattern.\n");
+  return true;
+}
+
+/// We are looking for the following pattern:
+/// loop:
+///   ...
+///   ...
+///   %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+///   ...
+///   %val.first = load <TYPE>, ptr %ptr.first.load, align 4
+///   %min.idx.ext = sext i32 %min.idx to i64
+///   %ptr.<TYPE>.min = getelementptr <TYPE>, ptr %0, i64 %min.idx.ext
+///   %ptr.second.load = getelementptr i8, ptr %ptr.<TYPE>.min, i64 -4
+///   %val.current.min = load <TYPE>, ptr %ptr.second.load, align 4
+///   %cmp = <CMP_INST> <TYPE> %val.first, %val.current.min
+///   ...
+///   %min.idx.next = select i1 %cmp, ..., i32 %min.idx
+///   ...
+///   ...
+///   br i1 ..., label %loop, ...
+///
+/// Any compare predicate is accepted, and %val.current.min may appear on
+/// either side of the compare. The transform memoizes one operand of the
+/// compare in a loop-carried PHI, which is sound regardless of whether the
+/// pattern computes a min, a max, or an equality check.
+bool GVNPass::recognizeMinFindingSelectPattern(SelectInst *Select) {
+  BasicBlock *BB = Select->getParent();
+
+  // Phase 1: Do trivial checks: loop / select / compare structural checks.
+  // Confirm BB is in a loop with a preheader, the select's condition is a
+  // compare, and both compare operands are loads of the same scalar type.
+  // On success: L, Preheader, Comparison, LoadType are populated.
+
+  // If the block is not in a loop, bail out.
+  Loop *L = LI->getLoopFor(BB);
+  if (!L)
+    return false;
+
+  // If preheader of the loop is not found, bail out.
+  BasicBlock *Preheader = L->getLoopPreheader();
+  if (!Preheader) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): Could not find loop preheader.\n");
+    return false;
+  }
+
+  // The transform plants a 2-input PHI in BB with incomings from Preheader
+  // and BB, so it is well-formed only for a single-block self-loop where
+  // BB is both header and latch.
+  if (BB != L->getHeader() || BB != L->getLoopLatch()) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): select's block is not a single-block "
+                         "self-loop (header == latch).\n");
+    return false;
+  }
+
+  Value *Condition = Select->getCondition();
+  CmpInst *Comparison = dyn_cast<CmpInst>(Condition);
+  if (!Comparison) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): Condition is not a comparison.\n");
+    return false;
+  }
+
+  // Both compare operands must be loads of the same type. The predicate
+  // itself is irrelevant: the rewrite memoizes one operand of the compare
+  // across iterations, and that is sound for any predicate.
+  Value *Op0 = Comparison->getOperand(0);
+  Value *Op1 = Comparison->getOperand(1);
+  if (!isa<LoadInst>(Op0) || !isa<LoadInst>(Op1)) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): Not both operands are loads.\n");
+    return false;
+  }
+  if (Op0->getType() != Op1->getType()) {
+    LLVM_DEBUG(
+        dbgs() << "GVN (minindx): Not both loads are of the same type.\n");
+    return false;
+  }
+  // The transform memoizes the hoistable load's value across iterations,
+  // dropping per-iteration freshness against concurrent writes. That is
+  // unsound for any non-simple (atomic or volatile) load.
+  if (!cast<LoadInst>(Op0)->isSimple() || !cast<LoadInst>(Op1)->isSimple()) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): atomic/volatile load operand.\n");
+    return false;
+  }
+  Type *LoadType = Op0->getType();
+
+  // Phase 2: Match the hoistable (RHS) load and canonicalize the compare.
+  // The rewrite needs one of the loads to be a typed GEP indexed by a
+  // sext'd phi (the recurrence of the candidate min-index). Try Op1 first
+  // to preserve the IR shape; if only Op0 matches, swapOperands() flips
+  // the predicate and moves the matched load to operand(1).
+  // On success: TypedGEP, RHSOffsetAP, IndexValPhi, Base are populated.
+  GetElementPtrInst *TypedGEP = nullptr;
+  APInt RHSOffsetAP;
+  PHINode *IndexValPhi = nullptr;
+  auto MatchHoistableLoad = [&](Value *LoadOp) -> bool {
+    Value *InnerGEP;
+    const APInt *OffsetAPInt;
+    if (!match(LoadOp,
+               m_Load(m_PtrAdd(m_Value(InnerGEP), m_APInt(OffsetAPInt)))))
+      return false;
+    auto *GEP = dyn_cast<GetElementPtrInst>(InnerGEP);
+    if (!GEP || GEP->getSourceElementType() != LoadType)
+      return false;
+    auto *SEInst = dyn_cast<SExtInst>(GEP->getOperand(1));
+    if (!SEInst)
+      return false;
+    if (!SEInst->getType()->isIntegerTy(64) ||
+        !SEInst->getOperand(0)->getType()->isIntegerTy(32))
+      return false;
+    auto *Phi = dyn_cast<PHINode>(SEInst->getOperand(0));
+    if (!Phi)
+      return false;
+    // The transform uses IndexValPhi->getIncomingValueForBlock(Preheader)
+    // and Phi nodes only have incoming entries for their parent's
+    // predecessors, so the Phi must live in BB (which has Preheader as a
+    // predecessor by Gate-3).
+    if (Phi->getParent() != BB)
+      return false;
+    TypedGEP = GEP;
+    RHSOffsetAP = *OffsetAPInt;
+    IndexValPhi = Phi;
+    return true;
+  };
+
+  if (!MatchHoistableLoad(Op1)) {
+    if (!MatchHoistableLoad(Op0)) {
+      LLVM_DEBUG(dbgs() << "GVN (minindx): No operand matches hoistable load "
+                           "pattern.\n");
+      return false;
+    }
+    Comparison->swapOperands();
+  }
+  Value *Base = TypedGEP->getPointerOperand();
+
+  // RAUW with the hoisted value would corrupt any extra in-loop use of
+  // the per-iteration load; require the load to feed only the compare.
+  auto *HoistableLoad = cast<LoadInst>(Comparison->getOperand(1));
+  if (!HoistableLoad->hasOneUse()) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): hoistable load has extra uses.\n");
+    return false;
+  }
+
+  // The transform builds a preheader GEP from Base; Base must dominate
+  // the preheader (i.e. be loop-invariant) for the result to verify.
+  if (!L->isLoopInvariant(Base)) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): Base is not loop-invariant.\n");
+    return false;
+  }
+
+  // Phase 3: Match the LHS load shape.
+  // Extract %inner and an optional byte offset C1 from the LHS load, then
+  // verify %inner is a typed `getelementptr T, Base, IV` sharing the same
+  // base pointer as the RHS load and using a single index.
+  // On success: LHSGEP, LHSIV, C1AP are populated.
+  Value *LHS = Comparison->getOperand(0);
+  Value *LHSInnerGEP;
+  const APInt *C1AP = nullptr;
+  // Accept either of these two shapes for the LHS load:
+  //   1) load T, ptr (getelementptr i8, %inner, C1)   -- byte offset C1 present
+  //   2) load T, ptr %inner                           -- no byte offset, C1 = 0
+  // %inner is the typed `getelementptr T, base, IV` checked just below.
+  if (!match(LHS, m_Load(m_PtrAdd(m_Value(LHSInnerGEP), m_APInt(C1AP)))) &&
+      !match(LHS, m_Load(m_Value(LHSInnerGEP)))) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): LHS load shape unsupported.\n");
+    return false;
+  }
+  // %inner (captured above) must be a typed `getelementptr T, base, IV`:
+  //   - element type T matches the load type,
+  //   - base pointer is the same array as the hoistable load,
+  //   - exactly one index (IV).
+  auto *LHSGEP = dyn_cast<GetElementPtrInst>(LHSInnerGEP);
+  if (!LHSGEP || LHSGEP->getSourceElementType() != LoadType ||
+      LHSGEP->getPointerOperand() != Base || LHSGEP->getNumIndices() != 1) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): LHS GEP shape unsupported.\n");
+    return false;
+  }
+  Value *LHSIV = LHSGEP->getOperand(1);
+
+  // Phase 4: Match the new-index arm of the Select.
+  // One of the Select operands must be trunc(add LHSIV, KStep) with a
+  // constant KStep -- this is the IV update produced when the compare
+  // selects the new minimum index.
+  // On success: KStepAP is populated.
+  const APInt *KStepAP = nullptr;
+  auto MatchNewIdx = [&](Value *SelOp) {
+    Value *IVNext;
+    return match(SelOp, m_Trunc(m_Value(IVNext))) &&
+           match(IVNext, m_Add(m_Specific(LHSIV), m_APInt(KStepAP)));
+  };
+  // The transform plants `select Cmp, LHS, KnownMin`, which assumes the
+  // new index is on the Select's true arm; reject the mirrored polarity.
+  Value *OtherArm = nullptr;
+  if (MatchNewIdx(Select->getTrueValue())) {
+    OtherArm = Select->getFalseValue();
+  } else {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): select's new-index arm is not the "
+                         "true arm.\n");
+    return false;
+  }
+
+  // The transform's "cmp-false keeps known_min" assumes the Select's
+  // non-new-index arm is IndexValPhi itself.
+  if (OtherArm != IndexValPhi) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): select's keep arm is not "
+                         "IndexValPhi.\n");
+    return false;
+  }
+
+  // The algebraic gate below assumes next iter's IndexValPhi takes the
+  // Select's result; enforce that here.
+  if (IndexValPhi->getIncomingValueForBlock(BB) != Select) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): IndexValPhi back-edge is not the "
+                         "select.\n");
+    return false;
+  }
+
+  // Phase 6: Algebraic soundness gate.
+  // The transform is sound only when, on the cmp-true arm, the next
+  // iteration's hoistable load reads from the same address this
+  // iteration's LHS load read. Otherwise propagating the LHS value as
+  // known_min would change the value seen next iteration.
+  //
+  // Both load addresses look like `Base + sizeof(T) * <iv> + <byte off>`.
+  // With C1 / C2 the byte offsets on the LHS / hoistable GEPs and KStep
+  // the addend in `iv.next = add iv, KStep`, the address-equality reduces
+  // to:
+  //     C1 - C2 == sizeof(T) * KStep.
+  // Evaluate that in pointer-index width.
+  const DataLayout &DL = Select->getDataLayout();
+  unsigned PtrBits = DL.getIndexTypeSizeInBits(Base->getType());
+  APInt C1 = C1AP ? C1AP->sextOrTrunc(PtrBits) : APInt::getZero(PtrBits);
+  APInt C2 = RHSOffsetAP.sextOrTrunc(PtrBits);
+  APInt KStep = KStepAP->sextOrTrunc(PtrBits);
+  APInt ElemBytes(PtrBits, DL.getTypeAllocSize(LoadType).getFixedValue());
+  APInt LHSDelta = C1 - C2, Stride = ElemBytes * KStep;
+  if (LHSDelta != Stride) {
+    LLVM_DEBUG(dbgs() << "GVN (minindx): offset/step relationship violated. "
+                      << "C1=" << C1 << " C2=" << C2 << " KStep=" << KStep
+                      << " sizeof(T)=" << ElemBytes
+                      << "; require C1 - C2 == sizeof(T) * KStep, got "
+                      << LHSDelta << " != " << Stride << ".\n");
+    return false;
+  }
+
+  LLVM_DEBUG(dbgs() << "GVN: Found minimum finding pattern in Block: "
+                    << Select->getParent()->getName() << ".\n");
+
+  Value *OffsetVal = ConstantInt::get(Select->getContext(), RHSOffsetAP);
+  return transformMinFindingSelectPattern(L, LoadType, Preheader, BB,
+                                          Comparison, Select, Base, IndexValPhi,
+                                          OffsetVal);
+}
+
 class llvm::gvn::GVNLegacyPass : public FunctionPass {
 public:
   static char ID; // Pass identification, replacement for typeid.
diff --git a/llvm/test/Transforms/GVN/PRE/gvn-min-pre-basic.ll b/llvm/test/Transforms/GVN/PRE/gvn-min-pre-basic.ll
new file mode 100644
index 0000000000000..1d222d3e2e809
--- /dev/null
+++ b/llvm/test/Transforms/GVN/PRE/gvn-min-pre-basic.ll
@@ -0,0 +1,236 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -gvn-enable-min-finding-select-pattern -passes=gvn -S < %s | FileCheck %s
+
+; Test the minimum finding pattern.
+; The following test case is extracted from rnflow app in Polyhedron benchmark suite.
+define void @test_gvn_min_pattern(ptr %0, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_min_pattern(
+; CHECK-SAME: ptr [[TMP0:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load float, ptr [[HOIST_GEP2]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi float [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_FLOAT_IV:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_FLOAT_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_FLOAT_MIN:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_FLOAT_MIN]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp contract olt float [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], float [[VAL_FIRST]], float [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:                                         ; preds = %loop, %entry
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.float.iv = getelementptr float, ptr %0, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.float.iv, i64 -8
+  %val.first = load float, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.float.min = getelementptr float, ptr %0, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.float.min, i64 -4
+  %val.current.min = load float, ptr %ptr.second.load, align 4
+  %cmp = fcmp contract olt float %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Positive test: Minimum finding pattern with i32 loads.
+define void @test_gvn_min_pattern_i32(ptr %arr, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_min_pattern_i32(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load i32, ptr [[HOIST_GEP2]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi i32 [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_I32_IV:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load i32, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_I32_MIN:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_MIN]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], i32 [[VAL_FIRST]], i32 [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:                                         ; preds = %loop, %entry
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.i32.iv = getelementptr i32, ptr %arr, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.i32.iv, i64 -8
+  %val.first = load i32, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.i32.min = getelementptr i32, ptr %arr, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.i32.min, i64 -4
+  %val.current.min = load i32, ptr %ptr.second.load, align 4
+  %cmp = icmp slt i32 %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Positive test: Minimum finding pattern with i64 loads.
+define void @test_gvn_min_pattern_i64(ptr %0, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_min_pattern_i64(
+; CHECK-SAME: ptr [[TMP0:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr i64, ptr [[TMP0]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -8
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load i64, ptr [[HOIST_GEP2]], align 8
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi i64 [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_I64_IV:%.*]] = getelementptr i64, ptr [[TMP0]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I64_IV]], i64 -16
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load i64, ptr [[PTR_FIRST_LOAD]], align 8
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_I64_MIN:%.*]] = getelementptr i64, ptr [[TMP0]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I64_MIN]], i64 -8
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], i64 [[VAL_FIRST]], i64 [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:                                         ; preds = %loop, %entry
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.i64.iv = getelementptr i64, ptr %0, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.i64.iv, i64 -16
+  %val.first = load i64, ptr %ptr.first.load, align 8
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.i64.min = getelementptr i64, ptr %0, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.i64.min, i64 -8
+  %val.current.min = load i64, ptr %ptr.second.load, align 8
+  %cmp = icmp slt i64 %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Positive test: the hoisted load preserves the user-declared
+; alignment of the original load (align 1 on a float load).
+define void @test_gvn_min_pattern_preserves_align(ptr %0, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_min_pattern_preserves_align(
+; CHECK-SAME: ptr [[TMP0:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load float, ptr [[HOIST_GEP2]], align 1
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi float [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_IV:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_FIRST_LOAD]], align 1
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_TYPED:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_TYPED]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], float [[VAL_FIRST]], float [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %loop.counter = phi i64 [ 3, %entry ], [ %loop.counter.next, %loop ]
+  %indvars.iv.i = phi i64 [ 3, %entry ], [ %indvars.iv.next.i, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.iv = getelementptr float, ptr %0, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.iv, i64 -8
+  %val.first = load float, ptr %ptr.first.load, align 1
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.typed = getelementptr float, ptr %0, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.typed, i64 -4
+  %val.current.min = load float, ptr %ptr.second.load, align 1
+  %cmp = fcmp olt float %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
diff --git a/llvm/test/Transforms/GVN/PRE/gvn-min-pre-negatives.ll b/llvm/test/Transforms/GVN/PRE/gvn-min-pre-negatives.ll
new file mode 100644
index 0000000000000..257957cb9d9f3
--- /dev/null
+++ b/llvm/test/Transforms/GVN/PRE/gvn-min-pre-negatives.ll
@@ -0,0 +1,1007 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -gvn-enable-min-finding-select-pattern -passes=gvn -S < %s | FileCheck %s
+
+declare void @external_function(ptr)
+
+; Negative test: Select not in a loop.
+define void @test_not_in_loop(ptr %arr) {
+; CHECK-LABEL: define void @test_not_in_loop(
+; CHECK-SAME: ptr [[ARR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[ARR]], align 4
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i64 1
+; CHECK-NEXT:    [[LOAD2:%.*]] = load float, ptr [[GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], [[LOAD2]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float [[LOAD2]]
+; CHECK-NEXT:    ret void
+;
+entry:
+  %gep1 = getelementptr float, ptr %arr, i32 0
+  %load1 = load float, ptr %gep1
+  %sext = sext i32 1 to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  ret void
+}
+
+; Negative test: Loop without preheader (multiple entry points).
+define void @test_no_preheader(ptr %arr, i32 %n, i1 %cond) {
+; CHECK-LABEL: define void @test_no_preheader(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP:.*]], label %[[OTHER_ENTRY:.*]]
+; CHECK:       [[OTHER_ENTRY]]:
+; CHECK-NEXT:    br label %[[LOOP]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ 0, %[[OTHER_ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i32 [[I]] to i64
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i64 [[SEXT]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br i1 %cond, label %loop, label %other_entry
+
+other_entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ 0, %other_entry ], [ %i.next, %loop ]
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %sext = sext i32 %i to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: Condition is not a CmpInst.
+define void @test_condition_not_cmp(ptr %arr, i32 %n, i1 %bool) {
+; CHECK-LABEL: define void @test_condition_not_cmp(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]], i1 [[BOOL:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i32 [[I]] to i64
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i64 [[SEXT]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %sext = sext i32 %i to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %sel = select i1 %bool, float %load1, float %load2
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: Not both operands are loads (constant RHS).
+define void @test_not_both_loads(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_not_both_loads(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[GEP1]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], 0.000000e+00
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float 0.000000e+00
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %cmp = fcmp olt float %load1, 0.0
+  %sel = select i1 %cmp, float %load1, float 0.0
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: Load doesn't match GEP(GEP(...))nested pattern.
+define void @test_wrong_gep_pattern(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_wrong_gep_pattern(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  ; Simple GEP, not nested GEP(GEP(...))
+  %gep2 = getelementptr float, ptr %arr, i32 %i
+  %load2 = load float, ptr %gep2
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: ZExt instead of SExt.
+define void @test_no_sext(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_no_sext(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[GEP1]], align 4
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i32 [[I]] to i64
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i64 [[ZEXT]]
+; CHECK-NEXT:    [[LOAD2:%.*]] = load float, ptr [[GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], [[LOAD2]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float [[LOAD2]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %zext = zext i32 %i to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %zext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: SExt with wrong types (i16->i32 instead of i32->i64).
+define void @test_wrong_sext_types(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_wrong_sext_types(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[GEP1]], align 4
+; CHECK-NEXT:    [[I16:%.*]] = trunc i32 [[I]] to i16
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i16 [[I16]] to i32
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i32 [[SEXT]]
+; CHECK-NEXT:    [[LOAD2:%.*]] = load float, ptr [[GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], [[LOAD2]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float [[LOAD2]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %i16 = trunc i32 %i to i16
+  %sext = sext i16 %i16 to i32
+  %gep2 = getelementptr float, ptr %arr, i32 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i32 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: IndexVal is a constant, not a PHI node.
+define void @test_index_is_constant(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_index_is_constant(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[GEP2_PHI_TRANS_INSERT:%.*]] = getelementptr float, ptr [[ARR]], i64 5
+; CHECK-NEXT:    [[LOAD2_PRE:%.*]] = load float, ptr [[GEP2_PHI_TRANS_INSERT]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[GEP1]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], [[LOAD2_PRE]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float [[LOAD2_PRE]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  ; IndexVal is a constant (5), not a PHI node!
+  %sext = sext i32 5 to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: Initial min index value is not 1.
+define void @test_initial_index_not_one(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_initial_index_not_one(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[GEP1]], align 4
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i64 [[SEXT]]
+; CHECK-NEXT:    [[LOAD2:%.*]] = load float, ptr [[GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], [[LOAD2]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float [[LOAD2]]
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[I]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  ; Initial value is 0, not 1!
+  %min.idx = phi i32 [ 0, %entry ], [ %min.idx.next, %loop ]
+
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %sext = sext i32 %min.idx to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %min.idx.next = select i1 %cmp, i32 %i, i32 %min.idx
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: Pattern recognized but hoisting not safe due to aliasing store in loop.
+define void @test_aliasing_store_in_loop(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_aliasing_store_in_loop(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[STORE_GEP:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[STORE_GEP]], align 4
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i64 [[SEXT]]
+; CHECK-NEXT:    [[LOAD2:%.*]] = load float, ptr [[GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float 0.000000e+00, [[LOAD2]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float 0.000000e+00, float [[LOAD2]]
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[I]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.next, %loop ]
+
+  ; This store aliases with the loads, preventing hoisting.
+  %store.gep = getelementptr float, ptr %arr, i32 %i
+  store float 0.0, ptr %store.gep
+
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %sext = sext i32 %min.idx to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %min.idx.next = select i1 %cmp, i32 %i, i32 %min.idx
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: Pattern recognized but BasePtr is not loop invariant.
+define void @test_non_invariant_base_ptr(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_non_invariant_base_ptr(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[BASE_PTR:%.*]] = phi ptr [ [[ARR]], %[[ENTRY]] ], [ [[BASE_PTR_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[BASE_PTR]], i32 [[I]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[GEP1]], align 4
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[BASE_PTR]], i64 [[SEXT]]
+; CHECK-NEXT:    [[LOAD2:%.*]] = load float, ptr [[GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], [[LOAD2]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float [[LOAD2]]
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[I]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[BASE_PTR_NEXT]] = getelementptr float, ptr [[BASE_PTR]], i32 1
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.next, %loop ]
+  ; Base pointer changes each iteration - not loop invariant!
+  %base.ptr = phi ptr [ %arr, %entry ], [ %base.ptr.next, %loop ]
+
+  %gep1 = getelementptr float, ptr %base.ptr, i32 %i
+  %load1 = load float, ptr %gep1
+  %sext = sext i32 %min.idx to i64
+  %gep2 = getelementptr float, ptr %base.ptr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %min.idx.next = select i1 %cmp, i32 %i, i32 %min.idx
+  %base.ptr.next = getelementptr float, ptr %base.ptr, i32 1
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: Pattern recognized but may-alias call in loop.
+define void @test_may_alias_call_in_loop(ptr %arr, i32 %n) {
+; CHECK-LABEL: define void @test_may_alias_call_in_loop(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    call void @external_function(ptr [[ARR]])
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr float, ptr [[ARR]], i32 [[I]]
+; CHECK-NEXT:    [[LOAD1:%.*]] = load float, ptr [[GEP1]], align 4
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr float, ptr [[ARR]], i64 [[SEXT]]
+; CHECK-NEXT:    [[LOAD2:%.*]] = load float, ptr [[GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[LOAD1]], [[LOAD2]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], float [[LOAD1]], float [[LOAD2]]
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[I]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[EXIT:%.*]] = icmp slt i32 [[I_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[EXIT]], label %[[LOOP]], label %[[EXIT_BLOCK:.*]]
+; CHECK:       [[EXIT_BLOCK]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.next, %loop ]
+
+  ; External function may modify memory.
+  call void @external_function(ptr %arr)
+
+  %gep1 = getelementptr float, ptr %arr, i32 %i
+  %load1 = load float, ptr %gep1
+  %sext = sext i32 %min.idx to i64
+  %gep2 = getelementptr float, ptr %arr, i64 %sext
+  %gep3 = getelementptr i8, ptr %gep2, i64 0
+  %load2 = load float, ptr %gep3
+  %cmp = fcmp olt float %load1, %load2
+  %sel = select i1 %cmp, float %load1, float %load2
+  %min.idx.next = select i1 %cmp, i32 %i, i32 %min.idx
+  %i.next = add i32 %i, 1
+  %exit = icmp slt i32 %i.next, %n
+  br i1 %exit, label %loop, label %exit_block
+
+exit_block:
+  ret void
+}
+
+; Negative test: byte offsets on the two load addresses do not satisfy
+; bytes_offset(val.first.gep) - bytes_offset(val.min.gep)
+;   == sizeof(LoadType) * step_of_iv_next
+; Here val.first has no i8-offset (C1 = 0), val.min has -4 (C2 = -4),
+; sizeof(float) = 4, and step = -1, so the required equality
+; (C1 - C2 == sizeof(T) * step) becomes 4 == -4 -- false. Transforming
+; would propagate this iter's val.first as next iter's known_min, but
+; next iter's val.min reads a different address. Recognizer must bail.
+define i32 @test_gvn_min_pattern_offset_mismatch(ptr %base) {
+; CHECK-LABEL: define i32 @test_gvn_min_pattern_offset_mismatch(
+; CHECK-SAME: ptr [[BASE:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CTR:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[CTR_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i64 [[IV]], -1
+; CHECK-NEXT:    [[PTR_IV:%.*]] = getelementptr float, ptr [[BASE]], i64 [[IV]]
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_IV]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[GEP_TYPED:%.*]] = getelementptr float, ptr [[BASE]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[GEP_OFFSET:%.*]] = getelementptr i8, ptr [[GEP_TYPED]], i64 -4
+; CHECK-NEXT:    [[VAL_MIN:%.*]] = load float, ptr [[GEP_OFFSET]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[VAL_FIRST]], [[VAL_MIN]]
+; CHECK-NEXT:    [[NEW_IDX:%.*]] = trunc nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEW_IDX]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[CTR_NEXT]] = add nsw i64 [[CTR]], -1
+; CHECK-NEXT:    [[COND:%.*]] = icmp samesign ugt i64 [[CTR]], 1
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i32 [[MIN_IDX_NEXT]]
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i64 [ 3, %entry ], [ %iv.next, %loop ]
+  %ctr = phi i64 [ 3, %entry ], [ %ctr.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.next, %loop ]
+  %iv.next = add nsw i64 %iv, -1
+  %ptr.iv = getelementptr float, ptr %base, i64 %iv
+  %val.first = load float, ptr %ptr.iv, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %gep.typed = getelementptr float, ptr %base, i64 %min.idx.ext
+  %gep.offset = getelementptr i8, ptr %gep.typed, i64 -4
+  %val.min = load float, ptr %gep.offset, align 4
+  %cmp = fcmp olt float %val.first, %val.min
+  %new.idx = trunc nsw i64 %iv.next to i32
+  %min.idx.next = select i1 %cmp, i32 %new.idx, i32 %min.idx
+  %ctr.next = add nsw i64 %ctr, -1
+  %cond = icmp samesign ugt i64 %ctr, 1
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret i32 %min.idx.next
+}
+
+; Negative test: select is in an inner block, not header-is-latch. Transform
+; must not fire.
+define void @test_select_block_not_header_is_latch(ptr %arr, i32 %initial_min_idx, i1 %go) {
+; CHECK-LABEL: define void @test_select_block_not_header_is_latch(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[INITIAL_MIN_IDX:%.*]], i1 [[GO:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[HEADER:.*]]
+; CHECK:       [[HEADER]]:
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[BODY:.*]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[BODY]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[BODY]] ]
+; CHECK-NEXT:    br i1 [[GO]], label %[[BODY]], label %[[EXIT:.*]]
+; CHECK:       [[BODY]]:
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_I32_IV:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load i32, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_I32_MIN:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_MIN]], i64 -4
+; CHECK-NEXT:    [[VAL_CURRENT_MIN:%.*]] = load i32, ptr [[PTR_SECOND_LOAD]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[VAL_FIRST]], [[VAL_CURRENT_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[HEADER]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %header
+
+header:
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %latch ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %latch ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %latch ]
+  br i1 %go, label %body, label %exit
+
+body:
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.i32.iv = getelementptr i32, ptr %arr, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.i32.iv, i64 -8
+  %val.first = load i32, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.i32.min = getelementptr i32, ptr %arr, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.i32.min, i64 -4
+  %val.current.min = load i32, ptr %ptr.second.load, align 4
+  %cmp = icmp slt i32 %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  br label %latch
+
+latch:
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %header, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test: hoistable load has an extra use in the loop.
+define { i32, float } @test_hoistable_load_has_extra_use(ptr %arr) {
+; CHECK-LABEL: define { i32, float } @test_hoistable_load_has_extra_use(
+; CHECK-SAME: ptr [[ARR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 5, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CTR:%.*]] = phi i64 [ 4, %[[ENTRY]] ], [ [[CTR_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[SUM:%.*]] = phi float [ 0.000000e+00, %[[ENTRY]] ], [ [[SUM_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i64 [[IV]], -1
+; CHECK-NEXT:    [[PTR_IV:%.*]] = getelementptr float, ptr [[ARR]], i64 [[IV]]
+; CHECK-NEXT:    [[PTR_FIRST:%.*]] = getelementptr i8, ptr [[PTR_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_FIRST]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[GEP_TYPED:%.*]] = getelementptr float, ptr [[ARR]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[GEP_OFFSET:%.*]] = getelementptr i8, ptr [[GEP_TYPED]], i64 -4
+; CHECK-NEXT:    [[VAL_MIN:%.*]] = load float, ptr [[GEP_OFFSET]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[VAL_FIRST]], [[VAL_MIN]]
+; CHECK-NEXT:    [[NEW_IDX:%.*]] = trunc nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEW_IDX]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[SUM_NEXT]] = fadd float [[SUM]], [[VAL_MIN]]
+; CHECK-NEXT:    [[CTR_NEXT]] = add nsw i64 [[CTR]], -1
+; CHECK-NEXT:    [[COND:%.*]] = icmp samesign ugt i64 [[CTR]], 1
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[R0:%.*]] = insertvalue { i32, float } poison, i32 [[MIN_IDX_NEXT]], 0
+; CHECK-NEXT:    [[R1:%.*]] = insertvalue { i32, float } [[R0]], float [[SUM_NEXT]], 1
+; CHECK-NEXT:    ret { i32, float } [[R1]]
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i64 [ 5, %entry ], [ %iv.next, %loop ]
+  %ctr = phi i64 [ 4, %entry ], [ %ctr.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.next, %loop ]
+  %sum = phi float [ 0.0, %entry ], [ %sum.next, %loop ]
+  %iv.next = add nsw i64 %iv, -1
+  %ptr.iv = getelementptr float, ptr %arr, i64 %iv
+  %ptr.first = getelementptr i8, ptr %ptr.iv, i64 -8
+  %val.first = load float, ptr %ptr.first, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %gep.typed = getelementptr float, ptr %arr, i64 %min.idx.ext
+  %gep.offset = getelementptr i8, ptr %gep.typed, i64 -4
+  %val.min = load float, ptr %gep.offset, align 4
+  %cmp = fcmp olt float %val.first, %val.min
+  %new.idx = trunc nsw i64 %iv.next to i32
+  %min.idx.next = select i1 %cmp, i32 %new.idx, i32 %min.idx
+  %sum.next = fadd float %sum, %val.min
+  %ctr.next = add nsw i64 %ctr, -1
+  %cond = icmp samesign ugt i64 %ctr, 1
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  %r0 = insertvalue { i32, float } poison, i32 %min.idx.next, 0
+  %r1 = insertvalue { i32, float } %r0, float %sum.next, 1
+  ret { i32, float } %r1
+}
+
+; Negative test: IndexValPhi's loop back-edge is not the Select.
+define void @test_indexvalphi_backedge_not_select(ptr %arr, ptr %sink) {
+; CHECK-LABEL: define void @test_indexvalphi_backedge_not_select(
+; CHECK-SAME: ptr [[ARR:%.*]], ptr [[SINK:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 5, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CTR:%.*]] = phi i64 [ 4, %[[ENTRY]] ], [ [[CTR_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i64 [[IV]], -1
+; CHECK-NEXT:    [[PTR_IV:%.*]] = getelementptr float, ptr [[ARR]], i64 [[IV]]
+; CHECK-NEXT:    [[PTR_FIRST:%.*]] = getelementptr i8, ptr [[PTR_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_FIRST]], align 4
+; CHECK-NEXT:    [[GEP_TYPED:%.*]] = getelementptr float, ptr [[ARR]], i64 1
+; CHECK-NEXT:    [[GEP_OFFSET:%.*]] = getelementptr i8, ptr [[GEP_TYPED]], i64 -4
+; CHECK-NEXT:    [[VAL_MIN:%.*]] = load float, ptr [[GEP_OFFSET]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[VAL_FIRST]], [[VAL_MIN]]
+; CHECK-NEXT:    [[NEW_IDX:%.*]] = trunc nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT:%.*]] = select i1 [[CMP]], i32 [[NEW_IDX]], i32 1
+; CHECK-NEXT:    [[SINK_IV:%.*]] = getelementptr i32, ptr [[SINK]], i64 [[CTR]]
+; CHECK-NEXT:    store i32 [[MIN_IDX_NEXT]], ptr [[SINK_IV]], align 4
+; CHECK-NEXT:    [[CTR_NEXT]] = add nsw i64 [[CTR]], -1
+; CHECK-NEXT:    [[COND:%.*]] = icmp samesign ugt i64 [[CTR]], 1
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i64 [ 5, %entry ], [ %iv.next, %loop ]
+  %ctr = phi i64 [ 4, %entry ], [ %ctr.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.alt, %loop ]
+  %iv.next = add nsw i64 %iv, -1
+  %ptr.iv = getelementptr float, ptr %arr, i64 %iv
+  %ptr.first = getelementptr i8, ptr %ptr.iv, i64 -8
+  %val.first = load float, ptr %ptr.first, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %gep.typed = getelementptr float, ptr %arr, i64 %min.idx.ext
+  %gep.offset = getelementptr i8, ptr %gep.typed, i64 -4
+  %val.min = load float, ptr %gep.offset, align 4
+  %cmp = fcmp olt float %val.first, %val.min
+  %new.idx = trunc nsw i64 %iv.next to i32
+  %min.idx.next = select i1 %cmp, i32 %new.idx, i32 %min.idx
+  %min.idx.alt = add nsw i32 %min.idx, 0
+  %sink.iv = getelementptr i32, ptr %sink, i64 %ctr
+  store i32 %min.idx.next, ptr %sink.iv, align 4
+  %ctr.next = add nsw i64 %ctr, -1
+  %cond = icmp samesign ugt i64 %ctr, 1
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test: Base pointer is loop-variant; hoisting GEP into the
+; preheader would use a value not dominating the preheader.
+define i32 @test_base_not_loop_invariant(ptr %arr) {
+; CHECK-LABEL: define i32 @test_base_not_loop_invariant(
+; CHECK-SAME: ptr [[ARR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CTR:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[CTR_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[BASE_PTR:%.*]] = phi ptr [ [[ARR]], %[[ENTRY]] ], [ [[BASE_PTR_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i64 [[IV]], -1
+; CHECK-NEXT:    [[PTR_IV:%.*]] = getelementptr float, ptr [[BASE_PTR]], i64 [[IV]]
+; CHECK-NEXT:    [[PTR_FIRST:%.*]] = getelementptr i8, ptr [[PTR_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_FIRST]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[GEP_TYPED:%.*]] = getelementptr float, ptr [[BASE_PTR]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[GEP_OFFSET:%.*]] = getelementptr i8, ptr [[GEP_TYPED]], i64 -4
+; CHECK-NEXT:    [[VAL_MIN:%.*]] = load float, ptr [[GEP_OFFSET]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[VAL_FIRST]], [[VAL_MIN]]
+; CHECK-NEXT:    [[NEW_IDX:%.*]] = trunc nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEW_IDX]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[BASE_PTR_NEXT]] = getelementptr float, ptr [[BASE_PTR]], i64 4
+; CHECK-NEXT:    [[CTR_NEXT]] = add nsw i64 [[CTR]], -1
+; CHECK-NEXT:    [[COND:%.*]] = icmp samesign ugt i64 [[CTR]], 1
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i32 [[MIN_IDX_NEXT]]
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i64 [ 3, %entry ], [ %iv.next, %loop ]
+  %ctr = phi i64 [ 3, %entry ], [ %ctr.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.next, %loop ]
+  %base.ptr = phi ptr [ %arr, %entry ], [ %base.ptr.next, %loop ]
+  %iv.next = add nsw i64 %iv, -1
+  %ptr.iv = getelementptr float, ptr %base.ptr, i64 %iv
+  %ptr.first = getelementptr i8, ptr %ptr.iv, i64 -8
+  %val.first = load float, ptr %ptr.first, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %gep.typed = getelementptr float, ptr %base.ptr, i64 %min.idx.ext
+  %gep.offset = getelementptr i8, ptr %gep.typed, i64 -4
+  %val.min = load float, ptr %gep.offset, align 4
+  %cmp = fcmp olt float %val.first, %val.min
+  %new.idx = trunc nsw i64 %iv.next to i32
+  %min.idx.next = select i1 %cmp, i32 %new.idx, i32 %min.idx
+  %base.ptr.next = getelementptr float, ptr %base.ptr, i64 4
+  %ctr.next = add nsw i64 %ctr, -1
+  %cond = icmp samesign ugt i64 %ctr, 1
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret i32 %min.idx.next
+}
+
+; Negative test: IndexValPhi is defined in an outer-loop block, not the
+; inner loop containing the select; the preheader-lookup would assert.
+define i32 @test_indexvalphi_in_outer_loop(ptr %a) {
+; CHECK-LABEL: define i32 @test_indexvalphi_in_outer_loop(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[OUTER:.*]]
+; CHECK:       [[OUTER]]:
+; CHECK-NEXT:    [[IDX_OUTER:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[IDX_NEXT:%.*]], %[[LOOP:.*]] ]
+; CHECK-NEXT:    [[IDX_EXT_PHI_TRANS_INSERT:%.*]] = sext i32 [[IDX_OUTER]] to i64
+; CHECK-NEXT:    [[RHS_GEP1_PHI_TRANS_INSERT:%.*]] = getelementptr i32, ptr [[A]], i64 [[IDX_EXT_PHI_TRANS_INSERT]]
+; CHECK-NEXT:    [[RHS_GEP2_PHI_TRANS_INSERT:%.*]] = getelementptr i8, ptr [[RHS_GEP1_PHI_TRANS_INSERT]], i64 -4
+; CHECK-NEXT:    [[RHS_PRE:%.*]] = load i32, ptr [[RHS_GEP2_PHI_TRANS_INSERT]], align 4
+; CHECK-NEXT:    br label %[[LOOP]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 1, %[[OUTER]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CNT:%.*]] = phi i32 [ 0, %[[OUTER]] ], [ [[CNT_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LHS_GEP:%.*]] = getelementptr i32, ptr [[A]], i64 [[IV]]
+; CHECK-NEXT:    [[LHS:%.*]] = load i32, ptr [[LHS_GEP]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[LHS]], [[RHS_PRE]]
+; CHECK-NEXT:    [[IV_NEXT]] = add i64 [[IV]], 1
+; CHECK-NEXT:    [[NEWIDX:%.*]] = trunc i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[IDX_NEXT]] = select i1 [[CMP]], i32 [[NEWIDX]], i32 [[IDX_OUTER]]
+; CHECK-NEXT:    [[CNT_NEXT]] = add i32 [[CNT]], 1
+; CHECK-NEXT:    [[CONT:%.*]] = icmp ult i32 [[CNT]], 1
+; CHECK-NEXT:    br i1 [[CONT]], label %[[LOOP]], label %[[OUTER]]
+;
+entry:
+  br label %outer
+outer:
+  %idx.outer = phi i32 [ 1, %entry ], [ %idx.next, %loop ]
+  br label %loop
+loop:
+  %iv = phi i64 [ 1, %outer ], [ %iv.next, %loop ]
+  %cnt = phi i32 [ 0, %outer ], [ %cnt.next, %loop ]
+  %lhs.gep = getelementptr i32, ptr %a, i64 %iv
+  %lhs = load i32, ptr %lhs.gep, align 4
+  %idx.ext = sext i32 %idx.outer to i64
+  %rhs.gep1 = getelementptr i32, ptr %a, i64 %idx.ext
+  %rhs.gep2 = getelementptr i8, ptr %rhs.gep1, i64 -4
+  %rhs = load i32, ptr %rhs.gep2, align 4
+  %cmp = icmp slt i32 %lhs, %rhs
+  %iv.next = add i64 %iv, 1
+  %newidx = trunc i64 %iv.next to i32
+  %idx.next = select i1 %cmp, i32 %newidx, i32 %idx.outer
+  %cnt.next = add i32 %cnt, 1
+  %cont = icmp ult i32 %cnt, 1
+  br i1 %cont, label %loop, label %outer
+}
+
+; Negative test: a post-load store in the loop body aliases the
+; hoistable load's location on the next iteration. Memoizing the
+; value across iterations would observe a stale value.
+define i32 @test_backedge_store_clobbers_hoistable_load(ptr %arr) {
+; CHECK-LABEL: define i32 @test_backedge_store_clobbers_hoistable_load(
+; CHECK-SAME: ptr [[ARR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CNT:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[CNT_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[PTR_CUR:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[IV]]
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load i32, ptr [[PTR_CUR]], align 4
+; CHECK-NEXT:    [[MIN_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_MIN_BASE:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[MIN_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND:%.*]] = getelementptr i8, ptr [[PTR_MIN_BASE]], i64 -4
+; CHECK-NEXT:    [[VAL_CURRENT_MIN:%.*]] = load i32, ptr [[PTR_SECOND]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[VAL_FIRST]], [[VAL_CURRENT_MIN]]
+; CHECK-NEXT:    [[IV_NEXT]] = add i64 [[IV]], 1
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    store i32 0, ptr [[PTR_CUR]], align 4
+; CHECK-NEXT:    [[CNT_NEXT]] = add i64 [[CNT]], 1
+; CHECK-NEXT:    [[CONT:%.*]] = icmp ult i64 [[CNT_NEXT]], 2
+; CHECK-NEXT:    br i1 [[CONT]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i32 [[MIN_IDX_NEXT]]
+;
+entry:
+  br label %loop
+loop:
+  %iv = phi i64 [ 1, %entry ], [ %iv.next, %loop ]
+  %cnt = phi i64 [ 0, %entry ], [ %cnt.next, %loop ]
+  %min.idx = phi i32 [ 1, %entry ], [ %min.idx.next, %loop ]
+  %ptr.cur = getelementptr i32, ptr %arr, i64 %iv
+  %val.first = load i32, ptr %ptr.cur, align 4
+  %min.ext = sext i32 %min.idx to i64
+  %ptr.min.base = getelementptr i32, ptr %arr, i64 %min.ext
+  %ptr.second = getelementptr i8, ptr %ptr.min.base, i64 -4
+  %val.current.min = load i32, ptr %ptr.second, align 4
+  %cmp = icmp slt i32 %val.first, %val.current.min
+  %iv.next = add i64 %iv, 1
+  %next.idx.trunc = trunc i64 %iv.next to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  store i32 0, ptr %ptr.cur, align 4
+  %cnt.next = add i64 %cnt, 1
+  %cont = icmp ult i64 %cnt.next, 2
+  br i1 %cont, label %loop, label %exit
+exit:
+  ret i32 %min.idx.next
+}
+
+; Negative test: hoistable load is unordered-atomic. Memoizing across
+; iterations would drop the per-iteration atomic semantics.
+define i32 @test_atomic_hoistable_load(ptr %a) {
+; CHECK-LABEL: define i32 @test_atomic_hoistable_load(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CTR:%.*]] = phi i64 [ 3, %[[ENTRY]] ], [ [[CTR_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i64 [[IV]], -1
+; CHECK-NEXT:    [[LHS_GEP:%.*]] = getelementptr i32, ptr [[A]], i64 [[IV]]
+; CHECK-NEXT:    [[LHS:%.*]] = load i32, ptr [[LHS_GEP]], align 4
+; CHECK-NEXT:    [[IDX_EXT:%.*]] = sext i32 [[IDX]] to i64
+; CHECK-NEXT:    [[RHS_GEP1:%.*]] = getelementptr i32, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT:    [[RHS_GEP2:%.*]] = getelementptr i8, ptr [[RHS_GEP1]], i64 -4
+; CHECK-NEXT:    [[RHS:%.*]] = load atomic i32, ptr [[RHS_GEP2]] unordered, align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[NEWIDX:%.*]] = trunc nsw i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[IDX_NEXT]] = select i1 [[CMP]], i32 [[NEWIDX]], i32 [[IDX]]
+; CHECK-NEXT:    [[CTR_NEXT]] = add nsw i64 [[CTR]], -1
+; CHECK-NEXT:    [[CONT:%.*]] = icmp samesign ugt i64 [[CTR]], 1
+; CHECK-NEXT:    br i1 [[CONT]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i32 [[IDX_NEXT]]
+;
+entry:
+  br label %loop
+loop:
+  %iv = phi i64 [ 3, %entry ], [ %iv.next, %loop ]
+  %ctr = phi i64 [ 3, %entry ], [ %ctr.next, %loop ]
+  %idx = phi i32 [ 1, %entry ], [ %idx.next, %loop ]
+  %iv.next = add nsw i64 %iv, -1
+  %lhs.gep = getelementptr i32, ptr %a, i64 %iv
+  %lhs = load i32, ptr %lhs.gep, align 4
+  %idx.ext = sext i32 %idx to i64
+  %rhs.gep1 = getelementptr i32, ptr %a, i64 %idx.ext
+  %rhs.gep2 = getelementptr i8, ptr %rhs.gep1, i64 -4
+  %rhs = load atomic i32, ptr %rhs.gep2 unordered, align 4
+  %cmp = icmp slt i32 %lhs, %rhs
+  %newidx = trunc nsw i64 %iv.next to i32
+  %idx.next = select i1 %cmp, i32 %newidx, i32 %idx
+  %ctr.next = add nsw i64 %ctr, -1
+  %cont = icmp samesign ugt i64 %ctr, 1
+  br i1 %cont, label %loop, label %exit
+exit:
+  ret i32 %idx.next
+}
+
+; Negative test: select's new-index sits on the false arm. The transform
+; plants `select Cmp, LHS, KnownMin`, so this polarity is unsupported.
+define i32 @test_new_index_on_false_arm(ptr %a) {
+; CHECK-LABEL: define i32 @test_new_index_on_false_arm(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CNT:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[CNT_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[IDX:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LHS_GEP:%.*]] = getelementptr i32, ptr [[A]], i64 [[IV]]
+; CHECK-NEXT:    [[LHS:%.*]] = load i32, ptr [[LHS_GEP]], align 4
+; CHECK-NEXT:    [[IDX_EXT:%.*]] = sext i32 [[IDX]] to i64
+; CHECK-NEXT:    [[RHS_GEP1:%.*]] = getelementptr i32, ptr [[A]], i64 [[IDX_EXT]]
+; CHECK-NEXT:    [[RHS_GEP2:%.*]] = getelementptr i8, ptr [[RHS_GEP1]], i64 -4
+; CHECK-NEXT:    [[RHS:%.*]] = load i32, ptr [[RHS_GEP2]], align 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[IV_NEXT]] = add i64 [[IV]], 1
+; CHECK-NEXT:    [[NEWIDX:%.*]] = trunc i64 [[IV_NEXT]] to i32
+; CHECK-NEXT:    [[IDX_NEXT]] = select i1 [[CMP]], i32 [[IDX]], i32 [[NEWIDX]]
+; CHECK-NEXT:    [[CNT_NEXT]] = add i32 [[CNT]], 1
+; CHECK-NEXT:    [[CONT:%.*]] = icmp ult i32 [[CNT]], 1
+; CHECK-NEXT:    br i1 [[CONT]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i32 [[IDX_NEXT]]
+;
+entry:
+  br label %loop
+loop:
+  %iv = phi i64 [ 1, %entry ], [ %iv.next, %loop ]
+  %cnt = phi i32 [ 0, %entry ], [ %cnt.next, %loop ]
+  %idx = phi i32 [ 1, %entry ], [ %idx.next, %loop ]
+  %lhs.gep = getelementptr i32, ptr %a, i64 %iv
+  %lhs = load i32, ptr %lhs.gep, align 4
+  %idx.ext = sext i32 %idx to i64
+  %rhs.gep1 = getelementptr i32, ptr %a, i64 %idx.ext
+  %rhs.gep2 = getelementptr i8, ptr %rhs.gep1, i64 -4
+  %rhs = load i32, ptr %rhs.gep2, align 4
+  %cmp = icmp slt i32 %lhs, %rhs
+  %iv.next = add i64 %iv, 1
+  %newidx = trunc i64 %iv.next to i32
+  %idx.next = select i1 %cmp, i32 %idx, i32 %newidx
+  %cnt.next = add i32 %cnt, 1
+  %cont = icmp ult i32 %cnt, 1
+  br i1 %cont, label %loop, label %exit
+exit:
+  ret i32 %idx.next
+}
diff --git a/llvm/test/Transforms/GVN/PRE/gvn-min-pre-predicates.ll b/llvm/test/Transforms/GVN/PRE/gvn-min-pre-predicates.ll
new file mode 100644
index 0000000000000..f5fe1e66ace20
--- /dev/null
+++ b/llvm/test/Transforms/GVN/PRE/gvn-min-pre-predicates.ll
@@ -0,0 +1,304 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -gvn-enable-min-finding-select-pattern -passes=gvn -S < %s | FileCheck %s
+
+; Positive test: non-LT predicate on the compare. The matched (hoistable)
+; load is on the RHS, so no operand swap is needed. Exercises that the
+; recognizer no longer rejects on predicate alone.
+define void @test_gvn_pattern_gt_predicate(ptr %0, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_pattern_gt_predicate(
+; CHECK-SAME: ptr [[TMP0:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load float, ptr [[HOIST_GEP2]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi float [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_FLOAT_IV:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_FLOAT_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_FLOAT_MIN:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_FLOAT_MIN]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp contract ogt float [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], float [[VAL_FIRST]], float [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.float.iv = getelementptr float, ptr %0, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.float.iv, i64 -8
+  %val.first = load float, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.float.min = getelementptr float, ptr %0, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.float.min, i64 -4
+  %val.current.min = load float, ptr %ptr.second.load, align 4
+  %cmp = fcmp contract ogt float %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Positive test: matched (hoistable) load is the LHS of the compare. The
+; recognizer must swap the compare's operands so the existing transform
+; (which expects the matched load at operand(1)) keeps working. The
+; user-written select keeps the new-index on its true arm.
+define void @test_gvn_pattern_matched_load_on_lhs(ptr %0, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_pattern_matched_load_on_lhs(
+; CHECK-SAME: ptr [[TMP0:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load float, ptr [[HOIST_GEP2]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi float [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_FLOAT_IV:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_FLOAT_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load float, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_FLOAT_MIN:%.*]] = getelementptr float, ptr [[TMP0]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_FLOAT_MIN]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp contract olt float [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], float [[VAL_FIRST]], float [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.float.iv = getelementptr float, ptr %0, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.float.iv, i64 -8
+  %val.first = load float, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.float.min = getelementptr float, ptr %0, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.float.min, i64 -4
+  %val.current.min = load float, ptr %ptr.second.load, align 4
+  %cmp = fcmp contract ogt float %val.current.min, %val.first
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Positive test: max-finding loop with `icmp sgt` (signed greater-than).
+; The recognizer is predicate-agnostic: the rewrite preserves the original
+; predicate, so transforming a max-finding loop is sound for the same
+; algebraic reason the min-finding loop is.
+define void @test_gvn_pattern_icmp_sgt(ptr %arr, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_pattern_icmp_sgt(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load i32, ptr [[HOIST_GEP2]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi i32 [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_I32_IV:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load i32, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_I32_MIN:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_MIN]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], i32 [[VAL_FIRST]], i32 [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.i32.iv = getelementptr i32, ptr %arr, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.i32.iv, i64 -8
+  %val.first = load i32, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.i32.min = getelementptr i32, ptr %arr, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.i32.min, i64 -4
+  %val.current.min = load i32, ptr %ptr.second.load, align 4
+  %cmp = icmp sgt i32 %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Positive test: unsigned-min loop with `icmp ult`. Documents that the
+; recognizer accepts unsigned predicates as well as signed.
+define void @test_gvn_pattern_icmp_ult(ptr %arr, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_pattern_icmp_ult(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load i32, ptr [[HOIST_GEP2]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[KNOWN_MIN:%.*]] = phi i32 [ [[HOISTED_LOAD]], %[[ENTRY]] ], [ [[CURRENT_MIN:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_I32_IV:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load i32, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_I32_MIN:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_MIN]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[VAL_FIRST]], [[KNOWN_MIN]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    [[CURRENT_MIN]] = select i1 [[CMP]], i32 [[VAL_FIRST]], i32 [[KNOWN_MIN]]
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.i32.iv = getelementptr i32, ptr %arr, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.i32.iv, i64 -8
+  %val.first = load i32, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.i32.min = getelementptr i32, ptr %arr, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.i32.min, i64 -4
+  %val.current.min = load i32, ptr %ptr.second.load, align 4
+  %cmp = icmp ult i32 %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Positive test: equality predicate. Degenerate but semantically valid -
+; the rewrite is sound for any predicate, including eq. Documents the
+; predicate-agnostic claim end-to-end. After the rewrite, downstream GVN
+; folds the loop-carried phi (it never changes value when cmp is eq), so
+; only the hoisted load survives - which still proves the in-loop val.min
+; load was eliminated.
+define void @test_gvn_pattern_icmp_eq(ptr %arr, i32 %initial_min_idx) {
+; CHECK-LABEL: define void @test_gvn_pattern_icmp_eq(
+; CHECK-SAME: ptr [[ARR:%.*]], i32 [[INITIAL_MIN_IDX:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[HOIST_SEXT:%.*]] = sext i32 [[INITIAL_MIN_IDX]] to i64
+; CHECK-NEXT:    [[HOIST_GEP1:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[HOIST_SEXT]]
+; CHECK-NEXT:    [[HOIST_GEP2:%.*]] = getelementptr i8, ptr [[HOIST_GEP1]], i64 -4
+; CHECK-NEXT:    [[HOISTED_LOAD:%.*]] = load i32, ptr [[HOIST_GEP2]], align 4
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[INDVARS_IV_I:%.*]] = phi i64 [ 1, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT_I:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[LOOP_COUNTER:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[LOOP_COUNTER_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[MIN_IDX:%.*]] = phi i32 [ [[INITIAL_MIN_IDX]], %[[ENTRY]] ], [ [[MIN_IDX_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[INDVARS_IV_NEXT_I]] = add nsw i64 [[INDVARS_IV_I]], -1
+; CHECK-NEXT:    [[PTR_I32_IV:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV_I]]
+; CHECK-NEXT:    [[PTR_FIRST_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_IV]], i64 -8
+; CHECK-NEXT:    [[VAL_FIRST:%.*]] = load i32, ptr [[PTR_FIRST_LOAD]], align 4
+; CHECK-NEXT:    [[MIN_IDX_EXT:%.*]] = sext i32 [[MIN_IDX]] to i64
+; CHECK-NEXT:    [[PTR_I32_MIN:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[MIN_IDX_EXT]]
+; CHECK-NEXT:    [[PTR_SECOND_LOAD:%.*]] = getelementptr i8, ptr [[PTR_I32_MIN]], i64 -4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[VAL_FIRST]], [[HOISTED_LOAD]]
+; CHECK-NEXT:    [[NEXT_IDX_TRUNC:%.*]] = trunc nsw i64 [[INDVARS_IV_NEXT_I]] to i32
+; CHECK-NEXT:    [[MIN_IDX_NEXT]] = select i1 [[CMP]], i32 [[NEXT_IDX_TRUNC]], i32 [[MIN_IDX]]
+; CHECK-NEXT:    [[LOOP_COUNTER_NEXT]] = add nsw i64 [[LOOP_COUNTER]], -1
+; CHECK-NEXT:    [[LOOP_CONTINUE:%.*]] = icmp samesign ugt i64 [[LOOP_COUNTER]], 1
+; CHECK-NEXT:    br i1 [[LOOP_CONTINUE]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %indvars.iv.i = phi i64 [ 1, %entry ], [ %indvars.iv.next.i, %loop ]
+  %loop.counter = phi i64 [ 0, %entry ], [ %loop.counter.next, %loop ]
+  %min.idx = phi i32 [ %initial_min_idx, %entry ], [ %min.idx.next, %loop ]
+  %indvars.iv.next.i = add nsw i64 %indvars.iv.i, -1
+  %ptr.i32.iv = getelementptr i32, ptr %arr, i64 %indvars.iv.i
+  %ptr.first.load = getelementptr i8, ptr %ptr.i32.iv, i64 -8
+  %val.first = load i32, ptr %ptr.first.load, align 4
+  %min.idx.ext = sext i32 %min.idx to i64
+  %ptr.i32.min = getelementptr i32, ptr %arr, i64 %min.idx.ext
+  %ptr.second.load = getelementptr i8, ptr %ptr.i32.min, i64 -4
+  %val.current.min = load i32, ptr %ptr.second.load, align 4
+  %cmp = icmp eq i32 %val.first, %val.current.min
+  %next.idx.trunc = trunc nsw i64 %indvars.iv.next.i to i32
+  %min.idx.next = select i1 %cmp, i32 %next.idx.trunc, i32 %min.idx
+  %loop.counter.next = add nsw i64 %loop.counter, -1
+  %loop.continue = icmp samesign ugt i64 %loop.counter, 1
+  br i1 %loop.continue, label %loop, label %exit
+
+exit:
+  ret void
+}



More information about the llvm-commits mailing list