[llvm] [FunctionnSpecializer] Do not mark function dead if any unexecutable call site exists (PR #154668)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 20 21:35:05 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-function-specialization
Author: XChy (XChy)
<details>
<summary>Changes</summary>
Fixes #<!-- -->153295.
For test case below:
```llvm
define i32 @<!-- -->caller() {
entry:
%call1 = call i32 @<!-- -->callee(i32 1)
%call2 = call i32 @<!-- -->callee(i32 0)
%cond = icmp eq i32 %call2, 0
br i1 %cond, label %common.ret, label %if.then
common.ret: ; preds = %entry
ret i32 0
if.then: ; preds = %entry
%unreachable_call = call i32 @<!-- -->callee(i32 2)
ret i32 %unreachable_call
}
define internal i32 @<!-- -->callee(i32 %ac) {
entry:
br label %ai
ai: ; preds = %ai, %entry
%add = or i32 0, 0
%cond = icmp eq i32 %ac, 1
br i1 %cond, label %aj, label %ai
aj: ; preds = %ai
ret i32 0
}
```
Before specialization, the SCCP solver determines that `unreachable_call` is unexecutable, as the value of `callee` can only be zero.
After specializing the call sites `call1` and `call2`, FnSpecializer announces `callee` is a dead function since all executable call sites are specialized. However, the unexecutable call sites can become executable again after solving specialized calls.
In this testcase, `call2` is considered `Overdefined` after specialization, making `cond` also `Overdefined`. Thus, `unreachable_call` becomes executable.
This patch prevents marking a function as dead and fully specialized if any unexecutable call site exists.
---
Full diff: https://github.com/llvm/llvm-project/pull/154668.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/IPO/FunctionSpecialization.cpp (+11-7)
- (added) llvm/test/Transforms/FunctionSpecialization/reachable-after-specialization.ll (+42)
``````````diff
diff --git a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
index c876a47ef2129..c799bb54a34b6 100644
--- a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
@@ -1167,15 +1167,17 @@ Constant *FunctionSpecializer::getCandidateConstant(Value *V) {
void FunctionSpecializer::updateCallSites(Function *F, const Spec *Begin,
const Spec *End) {
- // Collect the call sites that need updating.
+ // Collect the call sites that need updating and count ALL the call sites.
SmallVector<CallBase *> ToUpdate;
- for (User *U : F->users())
- if (auto *CS = dyn_cast<CallBase>(U);
- CS && CS->getCalledFunction() == F &&
- Solver.isBlockExecutable(CS->getParent()))
- ToUpdate.push_back(CS);
+ unsigned NCallsLeft = 0;
+ for (User *U : F->users()) {
+ if (auto *CS = dyn_cast<CallBase>(U); CS && CS->getCalledFunction() == F) {
+ NCallsLeft++;
+ if (Solver.isBlockExecutable(CS->getParent()))
+ ToUpdate.push_back(CS);
+ }
+ }
- unsigned NCallsLeft = ToUpdate.size();
for (CallBase *CS : ToUpdate) {
bool ShouldDecrementCount = CS->getFunction() == F;
@@ -1207,6 +1209,8 @@ void FunctionSpecializer::updateCallSites(Function *F, const Spec *Begin,
// If the function has been completely specialized, the original function
// is no longer needed. Mark it unreachable.
+ // NOTE: We cannot mark it unreachable if any unexecutable call site exists,
+ // as the unexecutable call site may become executable due to specialization.
if (NCallsLeft == 0 && Solver.isArgumentTrackedFunction(F)) {
Solver.markFunctionUnreachable(F);
FullySpecialized.insert(F);
diff --git a/llvm/test/Transforms/FunctionSpecialization/reachable-after-specialization.ll b/llvm/test/Transforms/FunctionSpecialization/reachable-after-specialization.ll
new file mode 100644
index 0000000000000..92685c5c72f2e
--- /dev/null
+++ b/llvm/test/Transforms/FunctionSpecialization/reachable-after-specialization.ll
@@ -0,0 +1,42 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=ipsccp --funcspec-min-function-size=1 -S < %s | FileCheck %s
+
+define i32 @caller() {
+; CHECK-LABEL: define i32 @caller() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALL1:%.*]] = call i32 @callee.specialized.1(i32 1)
+; CHECK-NEXT: [[CALL2:%.*]] = call i32 @callee.specialized.2(i32 0)
+; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 undef, 0
+; CHECK-NEXT: br i1 [[COND]], label %[[COMMON_RET:.*]], label %[[IF_THEN:.*]]
+; CHECK: [[COMMON_RET]]:
+; CHECK-NEXT: ret i32 0
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: [[UNREACHABLE_CALL:%.*]] = call i32 @callee.specialized.3(i32 2)
+; CHECK-NEXT: ret i32 undef
+;
+entry:
+ %call1 = call i32 @callee(i32 1)
+ %call2 = call i32 @callee(i32 0)
+ %cond = icmp eq i32 %call2, 0
+ br i1 %cond, label %common.ret, label %if.then
+
+common.ret: ; preds = %entry
+ ret i32 0
+
+if.then: ; preds = %entry
+ %unreachable_call = call i32 @callee(i32 2)
+ ret i32 %unreachable_call
+}
+
+define internal i32 @callee(i32 %ac) {
+entry:
+ br label %ai
+
+ai: ; preds = %ai, %entry
+ %add = or i32 0, 0
+ %cond = icmp eq i32 %ac, 1
+ br i1 %cond, label %aj, label %ai
+
+aj: ; preds = %ai
+ ret i32 0
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/154668
More information about the llvm-commits
mailing list