From 6bee710cd443c5fcf0c040b895c636e8ca7836d1 Mon Sep 17 00:00:00 2001 From: Alex Maclean Date: Mon, 9 Jun 2025 15:21:40 +0000 Subject: [PATCH 1/4] pre-commit tests --- .../InstCombine/canonicalize-const-to-bop.ll | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll b/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll index c08ec1bb7de0d..120232076f9a6 100644 --- a/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll +++ b/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll @@ -450,3 +450,82 @@ define i8 @umax_sgt(i8 %x) { %s = select i1 %cmp, i8 100, i8 %umax ret i8 %s } + +define i8 @add_sgt_nuw_nsw_safe(i8 %x) { +; CHECK-LABEL: define i8 @add_sgt_nuw_nsw_safe( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100) +; CHECK-NEXT: [[S:%.*]] = add nsw i8 [[TMP1]], 1 +; CHECK-NEXT: ret i8 [[S]] +; + %add = add nuw nsw i8 %x, 1 + %cmp = icmp sgt i8 %x, 100 + %s = select i1 %cmp, i8 101, i8 %add + ret i8 %s +} + +define i8 @add_sgt_nuw_only(i8 %x) { +; CHECK-LABEL: define i8 @add_sgt_nuw_only( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100) +; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], 50 +; CHECK-NEXT: ret i8 [[S]] +; + %add = add nuw nsw i8 %x, 50 + %cmp = icmp sgt i8 %x, 100 + %s = select i1 %cmp, i8 150, i8 %add + ret i8 %s +} + +define i8 @add_sgt_nsw_only(i8 %x) { +; CHECK-LABEL: define i8 @add_sgt_nsw_only( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100) +; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], -99 +; CHECK-NEXT: ret i8 [[S]] +; + %add = add nuw nsw i8 %x, -99 + %cmp = icmp sgt i8 %x, 100 + %s = select i1 %cmp, i8 1, i8 %add + ret i8 %s +} + + +define i8 @mul_ult_nuw_nsw_safe(i8 %x) { +; CHECK-LABEL: define i8 @mul_ult_nuw_nsw_safe( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 10) +; CHECK-NEXT: [[S:%.*]] = mul i8 [[TMP1]], 3 +; CHECK-NEXT: ret i8 [[S]] +; + %mul = mul nuw nsw i8 %x, 3 + %cmp = icmp ult i8 %x, 10 + %s = select i1 %cmp, i8 30, i8 %mul + ret i8 %s +} + +define i8 @mul_ult_nuw_only(i8 %x) { +; CHECK-LABEL: define i8 @mul_ult_nuw_only( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 10) +; CHECK-NEXT: [[S:%.*]] = mul i8 [[TMP1]], 25 +; CHECK-NEXT: ret i8 [[S]] +; + %mul = mul nuw nsw i8 %x, 25 + %cmp = icmp ult i8 %x, 10 + %s = select i1 %cmp, i8 250, i8 %mul + ret i8 %s +} + +define i8 @mul_ult_nsw_only(i8 %x) { +; CHECK-LABEL: define i8 @mul_ult_nsw_only( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 40) +; CHECK-NEXT: [[S:%.*]] = mul i8 [[TMP1]], -2 +; CHECK-NEXT: ret i8 [[S]] +; + %mul = mul nuw nsw i8 %x, -2 + %cmp = icmp ult i8 %x, 40 + %s = select i1 %cmp, i8 -80, i8 %mul + ret i8 %s +} From 16bc37458eee56af3dbe0f9ff1995c062a41fc05 Mon Sep 17 00:00:00 2001 From: Alex Maclean Date: Mon, 9 Jun 2025 15:55:13 +0000 Subject: [PATCH 2/4] [InstCombine] Preserve NSW/NUW flags when folding const BOp with min/max --- .../InstCombine/InstCombineInternal.h | 2 ++ .../InstCombine/InstCombineSelect.cpp | 36 ++++++++++++++----- .../InstCombine/canonicalize-const-to-bop.ll | 16 ++++----- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 334462d715f95..48f718bae29af 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -767,6 +767,8 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Value *A, Value *B, Instruction &Outer, SelectPatternFlavor SPF2, Value *C); Instruction *foldSelectInstWithICmp(SelectInst &SI, ICmpInst *ICI); + Value *foldSelectWithConstOpToBinOp(ICmpInst *Cmp, Value *TrueVal, + Value *FalseVal); Instruction *foldSelectValueEquivalence(SelectInst &SI, CmpInst &CI); bool replaceInInstruction(Value *V, Value *Old, Value *New, unsigned Depth = 0); diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index 8f46ae304353d..062b576369ce2 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -1823,9 +1823,9 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI, /// Fold `X Pred C1 ? X BOp C2 : C1 BOp C2` to `min/max(X, C1) BOp C2`. /// This allows for better canonicalization. -static Value *foldSelectWithConstOpToBinOp(ICmpInst *Cmp, Value *TrueVal, - Value *FalseVal, - IRBuilderBase &Builder) { +Value *InstCombinerImpl::foldSelectWithConstOpToBinOp(ICmpInst *Cmp, + Value *TrueVal, + Value *FalseVal) { Constant *C1, *C2, *C3; Value *X; CmpPredicate Predicate; @@ -1889,11 +1889,29 @@ static Value *foldSelectWithConstOpToBinOp(ICmpInst *Cmp, Value *TrueVal, return nullptr; } - Intrinsic::ID IntrinsicID = getMinMaxIntrinsic(SPF); - Value *Intrinsic = Builder.CreateBinaryIntrinsic(IntrinsicID, X, RHS); - return IsIntrinsic ? Builder.CreateBinaryIntrinsic(Opcode, Intrinsic, C2) - : Builder.CreateBinOp(Instruction::BinaryOps(Opcode), - Intrinsic, C2); + Intrinsic::ID MinMaxID = getMinMaxIntrinsic(SPF); + Value *MinMax = Builder.CreateBinaryIntrinsic(MinMaxID, X, RHS); + if (IsIntrinsic) + return Builder.CreateBinaryIntrinsic(Opcode, MinMax, C2); + + const auto BinOpc = Instruction::BinaryOps(Opcode); + Value *BinOp = Builder.CreateBinOp(BinOpc, MinMax, C2); + + // If we can attach no-wrap flags to the new instruction, do so if the + // old instruction had them and C1 BinOp C2 does not overflow. + if (Instruction *BinOpInst = dyn_cast(BinOp)) { + if (BinOpc == Instruction::Add || BinOpc == Instruction::Sub || + BinOpc == Instruction::Mul) { + Instruction *OldBinOp = cast(TrueVal); + if (OldBinOp->hasNoSignedWrap() && + willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned*/ true)) + BinOpInst->setHasNoSignedWrap(); + if (OldBinOp->hasNoUnsignedWrap() && + willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned*/ false)) + BinOpInst->setHasNoUnsignedWrap(); + } + } + return BinOp; } /// Visit a SelectInst that has an ICmpInst as its first operand. @@ -1968,7 +1986,7 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI, if (Value *V = foldAbsDiff(ICI, TrueVal, FalseVal, Builder)) return replaceInstUsesWith(SI, V); - if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal, Builder)) + if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal)) return replaceInstUsesWith(SI, V); return Changed ? &SI : nullptr; diff --git a/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll b/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll index 120232076f9a6..b3093a92624ae 100644 --- a/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll +++ b/llvm/test/Transforms/InstCombine/canonicalize-const-to-bop.ll @@ -5,7 +5,7 @@ define i8 @add_and_sgt(i8 %x) { ; CHECK-LABEL: define i8 @add_and_sgt( ; CHECK-SAME: i8 [[X:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 8) -; CHECK-NEXT: [[S:%.*]] = add nuw i8 [[TMP1]], 16 +; CHECK-NEXT: [[S:%.*]] = add nuw nsw i8 [[TMP1]], 16 ; CHECK-NEXT: ret i8 [[S]] ; %add = add nsw i8 %x, 16 @@ -155,7 +155,7 @@ define i8 @multi_use_cond_and_sel(i8 %x) { ; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X]], 8 ; CHECK-NEXT: call void @use(i1 [[CMP]]) ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 8) -; CHECK-NEXT: [[S:%.*]] = add nuw i8 [[TMP1]], 16 +; CHECK-NEXT: [[S:%.*]] = add nuw nsw i8 [[TMP1]], 16 ; CHECK-NEXT: call void @use_byte(i8 [[S]]) ; CHECK-NEXT: ret i8 [[S]] ; @@ -455,7 +455,7 @@ define i8 @add_sgt_nuw_nsw_safe(i8 %x) { ; CHECK-LABEL: define i8 @add_sgt_nuw_nsw_safe( ; CHECK-SAME: i8 [[X:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100) -; CHECK-NEXT: [[S:%.*]] = add nsw i8 [[TMP1]], 1 +; CHECK-NEXT: [[S:%.*]] = add nuw nsw i8 [[TMP1]], 1 ; CHECK-NEXT: ret i8 [[S]] ; %add = add nuw nsw i8 %x, 1 @@ -468,7 +468,7 @@ define i8 @add_sgt_nuw_only(i8 %x) { ; CHECK-LABEL: define i8 @add_sgt_nuw_only( ; CHECK-SAME: i8 [[X:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100) -; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], 50 +; CHECK-NEXT: [[S:%.*]] = add nuw i8 [[TMP1]], 50 ; CHECK-NEXT: ret i8 [[S]] ; %add = add nuw nsw i8 %x, 50 @@ -481,7 +481,7 @@ define i8 @add_sgt_nsw_only(i8 %x) { ; CHECK-LABEL: define i8 @add_sgt_nsw_only( ; CHECK-SAME: i8 [[X:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 100) -; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], -99 +; CHECK-NEXT: [[S:%.*]] = add nsw i8 [[TMP1]], -99 ; CHECK-NEXT: ret i8 [[S]] ; %add = add nuw nsw i8 %x, -99 @@ -495,7 +495,7 @@ define i8 @mul_ult_nuw_nsw_safe(i8 %x) { ; CHECK-LABEL: define i8 @mul_ult_nuw_nsw_safe( ; CHECK-SAME: i8 [[X:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 10) -; CHECK-NEXT: [[S:%.*]] = mul i8 [[TMP1]], 3 +; CHECK-NEXT: [[S:%.*]] = mul nuw nsw i8 [[TMP1]], 3 ; CHECK-NEXT: ret i8 [[S]] ; %mul = mul nuw nsw i8 %x, 3 @@ -508,7 +508,7 @@ define i8 @mul_ult_nuw_only(i8 %x) { ; CHECK-LABEL: define i8 @mul_ult_nuw_only( ; CHECK-SAME: i8 [[X:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 10) -; CHECK-NEXT: [[S:%.*]] = mul i8 [[TMP1]], 25 +; CHECK-NEXT: [[S:%.*]] = mul nuw i8 [[TMP1]], 25 ; CHECK-NEXT: ret i8 [[S]] ; %mul = mul nuw nsw i8 %x, 25 @@ -521,7 +521,7 @@ define i8 @mul_ult_nsw_only(i8 %x) { ; CHECK-LABEL: define i8 @mul_ult_nsw_only( ; CHECK-SAME: i8 [[X:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 40) -; CHECK-NEXT: [[S:%.*]] = mul i8 [[TMP1]], -2 +; CHECK-NEXT: [[S:%.*]] = mul nsw i8 [[TMP1]], -2 ; CHECK-NEXT: ret i8 [[S]] ; %mul = mul nuw nsw i8 %x, -2 From 8d6be44338fb5316c66c8d4d201ad6162f47c652 Mon Sep 17 00:00:00 2001 From: Alex MacLean Date: Tue, 10 Jun 2025 11:09:23 -0700 Subject: [PATCH 3/4] Update llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp Co-authored-by: Nikita Popov --- llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index 062b576369ce2..422cbf988d39e 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -1904,7 +1904,7 @@ Value *InstCombinerImpl::foldSelectWithConstOpToBinOp(ICmpInst *Cmp, BinOpc == Instruction::Mul) { Instruction *OldBinOp = cast(TrueVal); if (OldBinOp->hasNoSignedWrap() && - willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned*/ true)) + willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned=*/true)) BinOpInst->setHasNoSignedWrap(); if (OldBinOp->hasNoUnsignedWrap() && willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned*/ false)) From 7940b4685be1bc893da0de5db803b9ecbb7a754b Mon Sep 17 00:00:00 2001 From: Alex MacLean Date: Tue, 10 Jun 2025 11:09:30 -0700 Subject: [PATCH 4/4] Update llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp Co-authored-by: Nikita Popov --- llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index 422cbf988d39e..a185ab2e41a9f 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -1907,7 +1907,7 @@ Value *InstCombinerImpl::foldSelectWithConstOpToBinOp(ICmpInst *Cmp, willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned=*/true)) BinOpInst->setHasNoSignedWrap(); if (OldBinOp->hasNoUnsignedWrap() && - willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned*/ false)) + willNotOverflow(BinOpc, RHS, C2, *BinOpInst, /*IsSigned=*/false)) BinOpInst->setHasNoUnsignedWrap(); } }