[llvm] [IROutliner] Correctly Replace Outlined Constants with Arguments (PR #179885)

via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 5 00:35:43 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Sam Elliott (lenary)

<details>
<summary>Changes</summary>

Changes in a80a802d2f07 seem to have caused problems in the associated testcase. In this testcase, the block has two uses of `ptr null`, one in a load, and one in a GEP (within a `ConstantExpr`). The GEP is identical between the two outlined regions, so we should not be replacing its ConstantExpr with something based off an argument. The load is different between the outlined region, so its use of `ptr null` should be updated to use the argument.

Using a ValueMapper was not correct because the valuemapper maps all instances of a given value (in this case a constant), but we only want to modify some occurences of the constant.

The new code does not use the constant's use list (which the buggy change avoided), and instead assumes that GVN is only numbering operands of instructions, rather than iterating into ConstantExprs to number the entire expression.

Fixes #<!-- -->178874

---
Full diff: https://github.com/llvm/llvm-project/pull/179885.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/IPO/IROutliner.cpp (+14-9) 
- (added) llvm/test/Transforms/IROutliner/replace-null-in-constantexpr.ll (+54) 


``````````diff
diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp
index 6e1ca9c4cd2d6..c52a227c40e5c 100644
--- a/llvm/lib/Transforms/IPO/IROutliner.cpp
+++ b/llvm/lib/Transforms/IPO/IROutliner.cpp
@@ -24,7 +24,6 @@
 #include "llvm/IR/PassManager.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Transforms/IPO.h"
-#include "llvm/Transforms/Utils/ValueMapper.h"
 #include <optional>
 #include <vector>
 
@@ -1915,7 +1914,6 @@ replaceArgumentUses(OutlinableRegion &Region,
 void replaceConstants(OutlinableRegion &Region) {
   OutlinableGroup &Group = *Region.Parent;
   Function *OutlinedFunction = Group.OutlinedFunction;
-  ValueToValueMapTy VMap;
 
   // Iterate over the constants that need to be elevated into arguments
   for (std::pair<unsigned, Constant *> &Const : Region.AggArgToConstant) {
@@ -1923,16 +1921,23 @@ void replaceConstants(OutlinableRegion &Region) {
     assert(OutlinedFunction && "Overall Function is not defined?");
     Constant *CST = Const.second;
     Argument *Arg = Group.OutlinedFunction->getArg(AggArgIdx);
-    // Identify the argument it will be elevated to, and replace instances of
-    // that constant in the function.
-    VMap[CST] = Arg;
-    LLVM_DEBUG(dbgs() << "Replacing uses of constant " << *CST
+    LLVM_DEBUG(dbgs() << "Replacing instruction uses of constant " << *CST
                       << " in function " << *OutlinedFunction << " with "
                       << *Arg << '\n');
-  }
 
-  RemapFunction(*OutlinedFunction, VMap,
-                RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
+    for (auto &BB : *OutlinedFunction) {
+      for (Instruction &I : BB) {
+        // We specifically iterate over the direct operands of instructions, to
+        // avoid replacing constants that appear in `ConstantExpr`s
+        for (auto &U : I.operands()) {
+          if (auto *CCandidate = dyn_cast<Constant>(U)) {
+            if (CCandidate == CST)
+              U = Arg;
+          }
+        }
+      }
+    }
+  }
 }
 
 /// It is possible that there is a basic block that already performs the same
diff --git a/llvm/test/Transforms/IROutliner/replace-null-in-constantexpr.ll b/llvm/test/Transforms/IROutliner/replace-null-in-constantexpr.ll
new file mode 100644
index 0000000000000..d453993be626c
--- /dev/null
+++ b/llvm/test/Transforms/IROutliner/replace-null-in-constantexpr.ll
@@ -0,0 +1,54 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 6
+; RUN: opt -S -passes=verify,iroutliner -ir-outlining-no-cost < %s | FileCheck %s
+
+
+define ptr @outliner_crash(ptr %arrayidx9.i) {
+entry:
+  %cmp = icmp eq i8 0, 4
+  br i1 %cmp, label %if.then202.i, label %do.end213.i
+
+if.then202.i:                                     ; preds = %do.body.i
+  %0 = load i8, ptr null, align 1
+  %arrayidx14.i290 = getelementptr [2 x [3 x i8]], ptr getelementptr inbounds nuw (i8, ptr null, i32 4), i32 0
+  ret ptr %arrayidx14.i290
+
+do.end213.i:                                      ; preds = %do.body.i, %do.body.i
+  %1 = load i8, ptr %arrayidx9.i, align 1
+  %arrayidx14.i265 = getelementptr [2 x [3 x i8]], ptr getelementptr inbounds nuw (i8, ptr null, i32 4), i32 0
+  ret ptr null
+}
+; CHECK-LABEL: define ptr @outliner_crash(
+; CHECK-SAME: ptr [[ARRAYIDX9_I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[ARRAYIDX14_I290_LOC:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 0, 4
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_THEN202_I:.*]], label %[[DO_END213_I:.*]]
+; CHECK:       [[IF_THEN202_I]]:
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[ARRAYIDX14_I290_LOC]])
+; CHECK-NEXT:    call void @outlined_ir_func_0(ptr null, ptr [[ARRAYIDX14_I290_LOC]], i32 0)
+; CHECK-NEXT:    [[ARRAYIDX14_I290_RELOAD:%.*]] = load ptr, ptr [[ARRAYIDX14_I290_LOC]], align 8
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[ARRAYIDX14_I290_LOC]])
+; CHECK-NEXT:    ret ptr [[ARRAYIDX14_I290_RELOAD]]
+; CHECK:       [[DO_END213_I]]:
+; CHECK-NEXT:    call void @outlined_ir_func_0(ptr [[ARRAYIDX9_I]], ptr null, i32 -1)
+; CHECK-NEXT:    ret ptr null
+;
+;
+; CHECK-LABEL: define internal void @outlined_ir_func_0(
+; CHECK-SAME: ptr [[TMP0:%.*]], ptr [[TMP1:%.*]], i32 [[TMP2:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:  [[NEWFUNCROOT:.*:]]
+; CHECK-NEXT:    br label %[[IF_THEN202_I_TO_OUTLINE:.*]]
+; CHECK:       [[IF_THEN202_I_TO_OUTLINE]]:
+; CHECK-NEXT:    [[TMP3:%.*]] = load i8, ptr [[TMP0]], align 1
+; CHECK-NEXT:    [[ARRAYIDX14_I290:%.*]] = getelementptr [2 x [3 x i8]], ptr getelementptr inbounds nuw (i8, ptr null, i32 4), i32 0
+; CHECK-NEXT:    br label %[[IF_THEN202_I_AFTER_OUTLINE_EXITSTUB:.*]]
+; CHECK:       [[IF_THEN202_I_AFTER_OUTLINE_EXITSTUB]]:
+; CHECK-NEXT:    switch i32 [[TMP2]], label %[[FINAL_BLOCK_0:.*]] [
+; CHECK-NEXT:      i32 0, label %[[OUTPUT_BLOCK_0_0:.*]]
+; CHECK-NEXT:    ]
+; CHECK:       [[OUTPUT_BLOCK_0_0]]:
+; CHECK-NEXT:    store ptr [[ARRAYIDX14_I290]], ptr [[TMP1]], align 8
+; CHECK-NEXT:    br label %[[FINAL_BLOCK_0]]
+; CHECK:       [[FINAL_BLOCK_0]]:
+; CHECK-NEXT:    ret void
+;

``````````

</details>


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


More information about the llvm-commits mailing list