[llvm] [InstCombine] Avoid folding `select(umin(X, Y), X)` with non-constant mask (PR #143020)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 5 12:00:47 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Konstantin Bogdanov (thevar1able)
<details>
<summary>Changes</summary>
Fixes https://github.com/llvm/llvm-project/issues/139050.
Please suggest a better fix if I'm doing something wrong, I have no understanding of InstCombine optimization.
Missing optimization is a blocker for switching [ClickHouse build](https://github.com/ClickHouse/ClickHouse/pull/77352#issuecomment-2852255156) to `clang-20`.
The issue is that the following snippet:
```
declare i8 @<!-- -->llvm.umin.i8(i8, i8)
define i8 @<!-- -->masked_min_fold_bug(i8 %acc, i8 %val, i8 %mask) {
; CHECK-LABEL: @<!-- -->masked_min_fold_bug(
; CHECK: %cond = icmp eq i8 %mask, 0
; CHECK: %masked_val = select i1 %cond, i8 %val, i8 255
; CHECK: call i8 @<!-- -->llvm.umin.i8(i8 %acc, i8 %masked_val)
;
%cond = icmp eq i8 %mask, 0
%masked_val = select i1 %cond, i8 %val, i8 255
%res = call i8 @<!-- -->llvm.umin.i8(i8 %acc, i8 %masked_val)
ret i8 %res
}
```
is being optimized to the following code, which can not be vectorized later.
```
declare i8 @<!-- -->llvm.umin.i8(i8, i8) #<!-- -->0
define i8 @<!-- -->masked_min_fold_bug(i8 %acc, i8 %val, i8 %mask) {
%cond = icmp eq i8 %mask, 0
%1 = call i8 @<!-- -->llvm.umin.i8(i8 %acc, i8 %val)
%res = select i1 %cond, i8 %1, i8 %acc
ret i8 %res
}
attributes #<!-- -->0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```
Expected:
```
declare i8 @<!-- -->llvm.umin.i8(i8, i8) #<!-- -->0
define i8 @<!-- -->masked_min_fold_bug(i8 %acc, i8 %val, i8 %mask) {
%cond = icmp eq i8 %mask, 0
%masked_val = select i1 %cond, i8 %val, i8 -1
%res = call i8 @<!-- -->llvm.umin.i8(i8 %acc, i8 %masked_val)
ret i8 %res
}
attributes #<!-- -->0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
```
https://godbolt.org/z/cYMheKE5r
---
Full diff: https://github.com/llvm/llvm-project/pull/143020.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+19)
- (modified) llvm/test/Transforms/InstCombine/select.ll (+15)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index cfb4af391b540..930819d24393a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1654,6 +1654,25 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
if (Value *FreedOp = getFreedOperand(&CI, &TLI))
return visitFree(CI, FreedOp);
+ if (Function *F = CI.getCalledFunction()) {
+ if (F->getIntrinsicID() == Intrinsic::umin || F->getIntrinsicID() == Intrinsic::umax) {
+ for (Value *Arg : CI.args()) {
+ auto *SI = dyn_cast<SelectInst>(Arg);
+ if (!SI)
+ continue;
+
+ auto *TrueC = dyn_cast<Constant>(SI->getTrueValue());
+ auto *FalseC = dyn_cast<Constant>(SI->getFalseValue());
+
+ // Block only if the select is masking, e.g. select(cond, val, -1)
+ if ((TrueC && TrueC->isAllOnesValue()) || (FalseC && FalseC->isAllOnesValue())) {
+ LLVM_DEBUG(dbgs() << "InstCombine: skipping umin/umax folding for masked select\n");
+ return nullptr;
+ }
+ }
+ }
+ }
+
// If the caller function (i.e. us, the function that contains this CallInst)
// is nounwind, mark the call as nounwind, even if the callee isn't.
if (CI.getFunction()->doesNotThrow() && !CI.doesNotThrow()) {
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index e16f6ad2cfc9b..09cb84cde07ca 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -5047,3 +5047,18 @@ define <2 x ptr> @select_freeze_constant_expression_vector_gep(i1 %cond, <2 x pt
%sel = select i1 %cond, <2 x ptr> %y, <2 x ptr> %freeze
ret <2 x ptr> %sel
}
+
+declare i8 @llvm.umin.i8(i8, i8)
+
+define i8 @no_fold_masked_min(i8 %acc, i8 %val, i8 %mask) {
+; CHECK-LABEL: @no_fold_masked_min(
+; CHECK-NEXT: [[COND:%.*]] = icmp eq i8 [[MASK:%.*]], 0
+; CHECK-NEXT: [[MASKED_VAL:%.*]] = select i1 [[COND:%.*]], i8 [[VAL:%.*]], i8 -1
+; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.umin.i8(i8 [[ACC:%.*]], i8 [[MASKED_VAL:%.*]])
+; CHECK-NEXT: ret i8 [[RES]]
+;
+ %cond = icmp eq i8 %mask, 0
+ %masked_val = select i1 %cond, i8 %val, i8 -1
+ %res = call i8 @llvm.umin.i8(i8 %acc, i8 %masked_val)
+ ret i8 %res
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/143020
More information about the llvm-commits
mailing list