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

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 8 00:04:12 PST 2025


================
@@ -3330,6 +3334,128 @@ void GVNPass::assignValNumForDeadCode() {
   }
 }
 
+bool GVNPass::transformMinFindingSelectPattern(
+    Loop *L, Type *LoadType, BasicBlock *Preheader, BasicBlock *BB, Value *LHS,
+    Value *RHS, CmpInst *Comparison, SelectInst *Select, Value *BasePtr,
+    Value *IndexVal, Value *OffsetVal) {
+  // Hoist the chain of operations for the second load to preheader.
+  // %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
+  IRBuilder<> Builder(Preheader->getTerminator());
+
+  PHINode *Phi = dyn_cast<PHINode>(IndexVal);
+  if (!Phi) {
+    LLVM_DEBUG(dbgs() << "GVN: IndexVal is not a PHI node\n");
+    return false;
+  }
+
+  Value *InitialMinIndex = Phi->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 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
+  LoadInst *NewLoad = Builder.CreateLoad(LoadType, HoistedGEP2, "hoisted_load");
+
+  // Let the new load now take the place of the old load.
+  RHS->replaceAllUsesWith(NewLoad);
+  dyn_cast<LoadInst>(RHS)->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);
+  LLVM_DEBUG(dbgs() << "Transformed the code\n");
+  return true;
+}
+
+bool GVNPass::recognizeMinFindingSelectPattern(SelectInst *Select) {
+  Value *BasePtr = nullptr, *IndexVal = nullptr, *OffsetVal = nullptr;
+  LLVM_DEBUG(
+      dbgs()
+      << "GVN: Analyzing select instruction for minimum finding pattern.\n");
+  LLVM_DEBUG(dbgs() << "GVN: Select: " << *Select << "\n");
+  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");
+    return false;
+  }
+
+  // If preheader of the loop is not found, bail out.
+  BasicBlock *Preheader = L->getLoopPreheader();
+  if (!Preheader) {
+    LLVM_DEBUG(dbgs() << "GVN: Could not find loop preheader.\n");
+    return false;
+  }
+  Value *Condition = Select->getCondition();
+  CmpInst *Comparison = dyn_cast<CmpInst>(Condition);
+  if (!Comparison) {
+    LLVM_DEBUG(dbgs() << "GVN: Condition is not a comparison.\n");
+    return false;
+  }
+
+  // Check if this is less-than comparison.
+  CmpInst::Predicate Pred = Comparison->getPredicate();
+  if (Pred != CmpInst::ICMP_SLT && Pred != CmpInst::ICMP_ULT &&
+      Pred != CmpInst::FCMP_OLT && Pred != CmpInst::FCMP_ULT) {
----------------
nikic wrote:

This means that you will fail to handle the pattern where `x > y ? b : a` is used instead of `x < y ? a : b`. It's better to use swapped predicate in that case.

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


More information about the llvm-commits mailing list