[llvm] [FnSpecialization] Only accept codesize savings if strictly greater than the minimum amount (PR #164867)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 23 11:22:08 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Ryan Buchner (bababuck)

<details>
<summary>Changes</summary>

When `--funcspec-min-function-size` is set to a small value, any small function (provided passed a constant) will be scored favorably in terms of code size savings due to the rounding in integer division in the following statement:
```
auto IsProfitable = [&]() -> bool {
  ...
  if (CodeSizeSavings < MinCodeSizeSavings * FuncSize / 100)
    return false;
  ...
};
```
When MinCodeSizeSavings is set by default to 20, so if `FuncSize < 5` all allowed by the knob, then `MinCodeSizeSavings * FuncSize / 100` evaluates to 0. As a result, a CodeSizeSavings of `0` will pass this check and be possibly considered profitable.

I don't think this is generally an issue in the wild since `--funcspec-min-function-size` is not usually set to small values (it defaults to 500), but might be on inline cases.

Prior to this change, `unprofitable_spec` specializes (with `--funcspec-min-function-size=0`) despite not having any savings.
```
define i32 @<!-- -->main(i32 %y) {
  %spec = call i32 @<!-- -->unprofitable_spec(i32 1, i32 %y)
  ret i32 %spec
}

define i32 @<!-- -->unprofitable_spec(i32 %x, i32 %y) {
  %add = add i32 %x, %y
  ret i32 %add
}
```

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


5 Files Affected:

- (modified) llvm/lib/Transforms/IPO/FunctionSpecialization.cpp (+1-1) 
- (modified) llvm/test/Transforms/FunctionSpecialization/dead-gv-load.ll (+2-1) 
- (modified) llvm/test/Transforms/FunctionSpecialization/maxgrowth.ll (+1-1) 
- (added) llvm/test/Transforms/FunctionSpecialization/min-codesize-savings.ll (+28) 
- (modified) llvm/test/Transforms/FunctionSpecialization/recursive-penalty.ll (+2-1) 


``````````diff
diff --git a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
index 150a2dc5d48e2..6d4b2fb7e0065 100644
--- a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
@@ -995,7 +995,7 @@ bool FunctionSpecializer::findSpecializations(Function *F, unsigned FuncSize,
                    << (CodeSizeSavings * 100 / FuncSize) << "%)}\n");
 
         // Minimum codesize savings.
-        if (CodeSizeSavings < MinCodeSizeSavings * FuncSize / 100)
+        if (CodeSizeSavings <= MinCodeSizeSavings * FuncSize / 100)
           return false;
 
         // Lazily compute the Latency, to avoid unnecessarily computing BFI.
diff --git a/llvm/test/Transforms/FunctionSpecialization/dead-gv-load.ll b/llvm/test/Transforms/FunctionSpecialization/dead-gv-load.ll
index 134a79d349035..45e41bd1bdce9 100644
--- a/llvm/test/Transforms/FunctionSpecialization/dead-gv-load.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/dead-gv-load.ll
@@ -1,5 +1,6 @@
 ; 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
+; RUN: opt -passes=ipsccp  --funcspec-min-function-size=1 \
+; RUN: -funcspec-min-codesize-savings=1 -S < %s | FileCheck %s
 
 @gv = internal global ptr null
 
diff --git a/llvm/test/Transforms/FunctionSpecialization/maxgrowth.ll b/llvm/test/Transforms/FunctionSpecialization/maxgrowth.ll
index 82d1f7ae4a6e1..c07948489dd5e 100644
--- a/llvm/test/Transforms/FunctionSpecialization/maxgrowth.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/maxgrowth.ll
@@ -1,7 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 5
 ; RUN: opt -passes="ipsccp<func-spec>" -funcspec-min-function-size=1       \
 ; RUN:                                 -funcspec-for-literal-constant=true \
-; RUN:                                 -funcspec-min-codesize-savings=50   \
+; RUN:                                 -funcspec-min-codesize-savings=1    \
 ; RUN:                                 -funcspec-min-latency-savings=50    \
 ; RUN:                                 -funcspec-max-codesize-growth=1     \
 ; RUN:                                 -S < %s | FileCheck %s
diff --git a/llvm/test/Transforms/FunctionSpecialization/min-codesize-savings.ll b/llvm/test/Transforms/FunctionSpecialization/min-codesize-savings.ll
new file mode 100644
index 0000000000000..4547e0e009852
--- /dev/null
+++ b/llvm/test/Transforms/FunctionSpecialization/min-codesize-savings.ll
@@ -0,0 +1,28 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=ipsccp  --funcspec-min-function-size=0 -S < %s | FileCheck %s
+
+; Call to unprofitable_spec should not specialize since no folding can occur for the add
+; instruction as only 1 of the values is know. The score for this specialization will be 0.
+define i32 @main(i32 %y) {
+; CHECK-LABEL: define i32 @main(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SPEC:%.*]] = call i32 @unprofitable_spec(i32 1, i32 [[Y]])
+; CHECK-NEXT:    ret i32 [[SPEC]]
+;
+entry:
+  %spec = call i32 @unprofitable_spec(i32 1, i32 %y)
+  ret i32 %spec
+}
+
+define i32 @unprofitable_spec(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @unprofitable_spec(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  %add = add i32 %x, %y
+  ret i32 %add
+}
diff --git a/llvm/test/Transforms/FunctionSpecialization/recursive-penalty.ll b/llvm/test/Transforms/FunctionSpecialization/recursive-penalty.ll
index fc17387dec94d..409e7b870355d 100644
--- a/llvm/test/Transforms/FunctionSpecialization/recursive-penalty.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/recursive-penalty.ll
@@ -1,7 +1,8 @@
 ; REQUIRES: asserts
 ; RUN: opt -passes="ipsccp<func-spec>,inline,instcombine,simplifycfg" -S \
 ; RUN:     -funcspec-min-function-size=23 -funcspec-max-iters=100 \
-; RUN:     -debug-only=function-specialization < %s 2>&1 | FileCheck %s
+; RUN:     -debug-only=function-specialization -funcspec-min-codesize-savings=1 \
+; RUN:     < %s 2>&1 | FileCheck %s
 
 ; Make sure the number of specializations created are not
 ; linear to the number of iterations (funcspec-max-iters).

``````````

</details>


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


More information about the llvm-commits mailing list