[llvm] [IROutliner] do not outline non-uniform immarg constants (PR #195450)
via llvm-commits
llvm-commits at lists.llvm.org
Sat May 2 06:28:27 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Arda Serdar Pektezol (pektezol)
<details>
<summary>Changes</summary>
Fixes #<!-- -->194733
`IROutliner` can outline structurally similar regions by lifting non-uniform constants into arguments of the outlined function. This is invalid for operands passed to callee parameters marked `immarg`, because those operands must remain immediate constants at the call site.
A fuzzer found a case where `llvm.memcpy` calls differed only in the `i1 immarg` operand. The outliner replaced that constant with an outlined-function argument, producing invalid IR: `call void @<!-- -->llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 0, i1 %0)`
Fix this by detecting when a non-uniform constant operand corresponds to an `immarg` call parameter and rejecting the outlining group. Identical `immarg` constants are still outlineable, because they remain constants in the outlined function. Added a regression test covering this case, and also tested the existing `outline-memcpy.ll` regression to make sure the fix doesn't break existing behavior.
---
Full diff: https://github.com/llvm/llvm-project/pull/195450.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/IPO/IROutliner.cpp (+43)
- (added) llvm/test/Transforms/IROutliner/illegal-different-immarg.ll (+28)
``````````diff
diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp
index c1640f3d0e2a7..157fc51a9ab2d 100644
--- a/llvm/lib/Transforms/IPO/IROutliner.cpp
+++ b/llvm/lib/Transforms/IPO/IROutliner.cpp
@@ -579,11 +579,54 @@ collectRegionsConstants(OutlinableRegion &Region,
return ConstantsTheSame;
}
+/// Check whether \p Region contains a non-uniform constant operand that must
+/// remain an immediate argument.
+///
+/// \param Region - The region to check for non-uniform immarg constants.
+/// \param NotSame - The set of global value numbers that do not have the same
+/// constant in each region.
+/// \returns true if \p Region contains a non-uniform immarg constant, and false
+/// otherwise.
+static bool containsNonUniformImmArgConstant(OutlinableRegion &Region,
+ DenseSet<unsigned> &NotSame) {
+ IRSimilarityCandidate &C = *Region.Candidate;
+
+ for (IRInstructionData &ID : C) {
+ auto *CB = dyn_cast<CallBase>(ID.Inst);
+ if (!CB)
+ continue;
+
+ for (unsigned ArgIdx = 0, ArgEnd = CB->arg_size(); ArgIdx != ArgEnd;
+ ++ArgIdx) {
+ if (!CB->paramHasAttr(ArgIdx, Attribute::ImmArg))
+ continue;
+
+ Value *Arg = CB->getArgOperand(ArgIdx);
+ if (!isa<Constant>(Arg))
+ continue;
+
+ std::optional<unsigned> GVN = C.getGVN(Arg);
+ assert(GVN && "Expected a GVN for immarg operand?");
+ if (NotSame.contains(*GVN))
+ return true;
+ }
+ }
+
+ return false;
+}
+
void OutlinableGroup::findSameConstants(DenseSet<unsigned> &NotSame) {
DenseMap<unsigned, Constant *> GVNToConstant;
for (OutlinableRegion *Region : Regions)
collectRegionsConstants(*Region, GVNToConstant, NotSame);
+
+ for (OutlinableRegion *Region : Regions) {
+ if (!containsNonUniformImmArgConstant(*Region, NotSame))
+ continue;
+ IgnoreGroup = true;
+ return;
+ }
}
void OutlinableGroup::collectGVNStoreSets(Module &M) {
diff --git a/llvm/test/Transforms/IROutliner/illegal-different-immarg.ll b/llvm/test/Transforms/IROutliner/illegal-different-immarg.ll
new file mode 100644
index 0000000000000..04f1ef7165869
--- /dev/null
+++ b/llvm/test/Transforms/IROutliner/illegal-different-immarg.ll
@@ -0,0 +1,28 @@
+; RUN: opt -S -passes=verify,iroutliner -ir-outlining-no-cost < %s | FileCheck %s
+
+; Do not outline structurally similar regions when doing so would replace a
+; non-uniform immarg constant with an outlined-function argument.
+
+declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg)
+
+define ptr @PR194733() {
+entry:
+ br i1 false, label %then, label %else
+
+then:
+ %0 = load ptr, ptr null, align 8
+ call void @llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 0, i1 false)
+ ret ptr null
+
+else:
+ %1 = load ptr, ptr null, align 8
+ call void @llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 0, i1 true)
+ %2 = load ptr, ptr null, align 8
+ call void @llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 0, i1 false)
+ ret ptr null
+}
+; CHECK-LABEL: define ptr @PR194733(
+; CHECK-NOT: outlined_ir_func
+; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 0, i1 false)
+; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 0, i1 true)
+; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 0, i1 false)
``````````
</details>
https://github.com/llvm/llvm-project/pull/195450
More information about the llvm-commits
mailing list