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

Madhur Amilkanthwar via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 28 08:27:43 PST 2026


================
@@ -3330,6 +3333,235 @@ void GVNPass::assignValNumForDeadCode() {
   }
 }
 
+// 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
+//
+// *** 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, Value *LHS,
+    Value *LoadVal, CmpInst *Comparison, SelectInst *Select, Value *BasePtr,
+    Value *IndexVal, Value *OffsetVal) {
+
+  assert(IndexVal && "IndexVal is null");
+  AAResults *AA = VN.getAliasAnalysis();
+  assert(AA && "AA is null");
+
+  IRBuilder<> Builder(Preheader->getTerminator());
+  Value *InitialMinIndex =
+      dyn_cast<PHINode>(IndexVal)->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");
+
+  MemoryLocation NewLoc = MemoryLocation(
+      HoistedGEP2,
+      LocationSize::precise(
+          L->getHeader()->getDataLayout().getTypeStoreSize(LoadType)));
+  // Check if any instruction in the loop clobbers this location.
+  bool CanHoist = true;
+  for (BasicBlock *BB : L->blocks()) {
+    for (Instruction &I : *BB) {
+      if (I.mayWriteToMemory()) {
+        // Check if this instruction may clobber our hoisted load.
+        ModRefInfo MRI = AA->getModRefInfo(&I, NewLoc);
+        if (isModOrRefSet(MRI)) {
+          LLVM_DEBUG(dbgs() << "GVN: Cannot hoist - may be clobbered by: " << I
+                            << "\n");
+          CanHoist = false;
+          break;
+        }
+      }
+    }
+    if (!CanHoist)
+      break;
+  }
+  if (!CanHoist) {
+    LLVM_DEBUG(dbgs() << "GVN: Cannot hoist - may be clobbered by some "
+                         "instruction in the loop.\n");
+    return false;
+  }
+
+  // 4. hoisted_load = load float, ptr HoistedGEP2
+  LoadInst *NewLoad = Builder.CreateLoad(LoadType, HoistedGEP2, "hoisted_load");
+
+  // Let the new load now take the place of the old load.
+  LoadVal->replaceAllUsesWith(NewLoad);
+  dyn_cast<LoadInst>(LoadVal)->eraseFromParent();
+
+  // 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);
+
+  if (MSSAU) {
+    auto *OrigUse =
+        MSSAU->getMemorySSA()->getMemoryAccess(dyn_cast<Instruction>(LoadVal));
+    if (OrigUse) {
+      MemoryAccess *DefiningAccess = OrigUse->getDefiningAccess();
+      MSSAU->createMemoryAccessInBB(NewLoad, DefiningAccess, Preheader,
+                                    MemorySSA::BeforeTerminator);
+    }
+  }
+  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, ...
+bool GVNPass::recognizeMinFindingSelectPattern(SelectInst *Select) {
+  IRBuilder<> Builder(Select);
+  Value *BasePtr = nullptr, *IndexVal = nullptr, *OffsetVal = nullptr,
+        *SExt = nullptr;
+  BasicBlock *BB = Select->getParent();
+
+  // If the block is not in a loop, bail out.
+  Loop *L = LI->getLoopFor(BB);
+  if (!L) {
+    LLVM_DEBUG(dbgs() << "GVN: Could not find loop.\n");
----------------
madhur13490 wrote:

Done

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


More information about the llvm-commits mailing list