[llvm] [InstCombine] Fold umin(mul/add(zext, zext), max) into overflow intrinsics (PR #221809)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 7 15:51:24 PDT 2026
https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/221809
>From 3d54b00e885184fd024cf19e819802e5d2711600 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Mon, 7 Sep 2026 15:13:12 -0400
Subject: [PATCH 1/2] [InstCombine] Pre-commit tests for saturating overflow
folds
---
.../InstCombine/umin-mul-overflow.ll | 169 ++++++++++++++++++
1 file changed, 169 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/umin-mul-overflow.ll
diff --git a/llvm/test/Transforms/InstCombine/umin-mul-overflow.ll b/llvm/test/Transforms/InstCombine/umin-mul-overflow.ll
new file mode 100644
index 0000000000000..938c04c63bbee
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/umin-mul-overflow.ll
@@ -0,0 +1,169 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i32 @test_mul_sat_32(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_mul_sat_32(
+; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
+; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[ZA]], [[ZB]]
+; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[MUL]], i64 4294967295)
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i64 [[UMIN]] to i32
+; CHECK-NEXT: ret i32 [[TRUNC]]
+;
+ %za = zext i32 %a to i64
+ %zb = zext i32 %b to i64
+ %mul = mul nuw i64 %za, %zb
+ %umin = call i64 @llvm.umin.i64(i64 %mul, i64 4294967295)
+ %trunc = trunc i64 %umin to i32
+ ret i32 %trunc
+}
+
+define i64 @test_mul_sat_64(i64 %a, i64 %b) {
+; CHECK-LABEL: @test_mul_sat_64(
+; CHECK-NEXT: [[ZA:%.*]] = zext i64 [[A:%.*]] to i128
+; CHECK-NEXT: [[ZB:%.*]] = zext i64 [[B:%.*]] to i128
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i128 [[ZA]], [[ZB]]
+; CHECK-NEXT: [[UMIN:%.*]] = call i128 @llvm.umin.i128(i128 [[MUL]], i128 18446744073709551615)
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i128 [[UMIN]] to i64
+; CHECK-NEXT: ret i64 [[TRUNC]]
+;
+ %za = zext i64 %a to i128
+ %zb = zext i64 %b to i128
+ %mul = mul nuw i128 %za, %zb
+ %umin = call i128 @llvm.umin.i128(i128 %mul, i128 18446744073709551615)
+ %trunc = trunc i128 %umin to i64
+ ret i64 %trunc
+}
+
+declare i64 @llvm.umin.i64(i64, i64)
+declare i128 @llvm.umin.i128(i128, i128)
+
+define i32 @test_add_sat_32(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_add_sat_32(
+; CHECK-NEXT: [[TRUNC:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[A:%.*]], i32 [[B:%.*]])
+; CHECK-NEXT: ret i32 [[TRUNC]]
+;
+ %za = zext i32 %a to i64
+ %zb = zext i32 %b to i64
+ %add = add nuw i64 %za, %zb
+ %umin = call i64 @llvm.umin.i64(i64 %add, i64 4294967295)
+ %trunc = trunc i64 %umin to i32
+ ret i32 %trunc
+}
+
+define i64 @test_add_sat_64(i64 %a, i64 %b) {
+; CHECK-LABEL: @test_add_sat_64(
+; CHECK-NEXT: [[TRUNC:%.*]] = call i64 @llvm.uadd.sat.i64(i64 [[A:%.*]], i64 [[B:%.*]])
+; CHECK-NEXT: ret i64 [[TRUNC]]
+;
+ %za = zext i64 %a to i128
+ %zb = zext i64 %b to i128
+ %add = add nuw i128 %za, %zb
+ %umin = call i128 @llvm.umin.i128(i128 %add, i128 18446744073709551615)
+ %trunc = trunc i128 %umin to i64
+ ret i64 %trunc
+}
+
+define i9 @test_add_nsw_poison(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_add_nsw_poison(
+; CHECK-NEXT: [[Y1:%.*]] = zext i8 [[B1:%.*]] to i9
+; CHECK-NEXT: [[Y:%.*]] = zext i8 [[B:%.*]] to i9
+; CHECK-NEXT: [[P:%.*]] = add nuw nsw i9 [[Y1]], [[Y]]
+; CHECK-NEXT: ret i9 [[P]]
+;
+ %x = zext i8 %a to i9
+ %y = zext i8 %b to i9
+ %p = add nsw i9 %x, %y
+ %r = call i9 @llvm.umin.i9(i9 %p, i9 255)
+ ret i9 %r
+}
+
+; Negative test: mul has multi-use, fold should NOT fire
+define i64 @test_mul_multiuse(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_mul_multiuse(
+; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
+; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[ZA]], [[ZB]]
+; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[MUL]], i64 4294967295)
+; CHECK-NEXT: [[OTHER:%.*]] = add i64 [[MUL]], 1
+; CHECK-NEXT: [[RES:%.*]] = add i64 [[OTHER]], [[UMIN]]
+; CHECK-NEXT: ret i64 [[RES]]
+;
+ %za = zext i32 %a to i64
+ %zb = zext i32 %b to i64
+ %mul = mul nuw i64 %za, %zb
+ %umin = call i64 @llvm.umin.i64(i64 %mul, i64 4294967295)
+ %trunc = trunc i64 %umin to i32
+ %other = add i64 %mul, 1
+ %res = add i64 %other, %umin
+ ret i64 %res
+}
+
+; Negative test: add has multi-use, fold should NOT fire
+define i64 @test_add_multiuse(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_add_multiuse(
+; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
+; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
+; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[ZA]], [[ZB]]
+; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 4294967295)
+; CHECK-NEXT: [[OTHER:%.*]] = add nuw nsw i64 [[ADD]], 1
+; CHECK-NEXT: [[RES:%.*]] = add nuw nsw i64 [[OTHER]], [[UMIN]]
+; CHECK-NEXT: ret i64 [[RES]]
+;
+ %za = zext i32 %a to i64
+ %zb = zext i32 %b to i64
+ %add = add nuw i64 %za, %zb
+ %umin = call i64 @llvm.umin.i64(i64 %add, i64 4294967295)
+ %trunc = trunc i64 %umin to i32
+ %other = add i64 %add, 1
+ %res = add i64 %other, %umin
+ ret i64 %res
+}
+
+; Positive test: mul has multi-use, but they are all truncs <= SrcBitWidth
+define i32 @test_mul_multiuse_trunc(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_mul_multiuse_trunc(
+; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
+; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[ZA]], [[ZB]]
+; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[MUL]], i64 4294967295)
+; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i64 [[UMIN]] to i32
+; CHECK-NEXT: [[UMUL_VALUE:%.*]] = trunc i64 [[MUL]] to i32
+; CHECK-NEXT: [[ZEXT:%.*]] = and i32 [[UMUL_VALUE]], 65535
+; CHECK-NEXT: [[RES:%.*]] = add i32 [[ZEXT]], [[TRUNC1]]
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %za = zext i32 %a to i64
+ %zb = zext i32 %b to i64
+ %mul = mul nuw i64 %za, %zb
+ %umin = call i64 @llvm.umin.i64(i64 %mul, i64 4294967295)
+ %trunc1 = trunc i64 %umin to i32
+ %trunc2 = trunc i64 %mul to i16
+ %zext = zext i16 %trunc2 to i32
+ %res = add i32 %trunc1, %zext
+ ret i32 %res
+}
+
+; Positive test: add has multi-use, but they are all truncs <= SrcBitWidth
+define i32 @test_add_multiuse_trunc(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_add_multiuse_trunc(
+; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
+; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
+; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[ZA]], [[ZB]]
+; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 4294967295)
+; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i64 [[UMIN]] to i32
+; CHECK-NEXT: [[UADD:%.*]] = trunc i64 [[ADD]] to i32
+; CHECK-NEXT: [[ZEXT:%.*]] = and i32 [[UADD]], 65535
+; CHECK-NEXT: [[RES:%.*]] = add i32 [[ZEXT]], [[TRUNC1]]
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %za = zext i32 %a to i64
+ %zb = zext i32 %b to i64
+ %add = add nuw i64 %za, %zb
+ %umin = call i64 @llvm.umin.i64(i64 %add, i64 4294967295)
+ %trunc1 = trunc i64 %umin to i32
+ %trunc2 = trunc i64 %add to i16
+ %zext = zext i16 %trunc2 to i32
+ %res = add i32 %trunc1, %zext
+ ret i32 %res
+}
>From 2452ec1d0d65fcc7b2ee47da94a4ae4a077375e1 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Mon, 7 Sep 2026 16:37:47 -0400
Subject: [PATCH 2/2] [InstCombine] Fold umin(mul/add(zext, zext), max) into
overflow intrinsics
---
.../InstCombine/InstCombineCalls.cpp | 53 ++++++++++++++++++
.../InstCombine/InstCombineCompares.cpp | 54 +-----------------
.../InstCombine/InstCombineInternal.h | 8 +++
.../InstCombine/InstructionCombining.cpp | 56 +++++++++++++++++++
.../InstCombine/umin-mul-overflow.ll | 47 +++++++---------
5 files changed, 139 insertions(+), 79 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index e888281baeae2..0e2ca175162d2 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -2037,6 +2037,55 @@ static Value *foldCmpIntrinsicOfExtended(IntrinsicInst *II,
return Builder.CreateIntrinsic(II->getType(), NewIID, {X, Y});
}
+static Instruction *foldUMinSatOverflow(IntrinsicInst &II, Value *I0, Value *I1,
+ InstCombiner::BuilderTy &Builder,
+ InstCombinerImpl &IC) {
+ // umin(mul/add(zext(A), zext(B)), MaxVal) ->
+ // select(umul/uadd.with.overflow(A, B), MaxVal, zext(mul/add))
+ const APInt *C;
+ Value *X, *Y;
+ if (match(I1, m_APInt(C)) &&
+ match(I0, m_BinOp(m_ZExt(m_Value(X)), m_ZExt(m_Value(Y))))) {
+ unsigned Opc = cast<BinaryOperator>(I0)->getOpcode();
+ if (Opc == Instruction::Mul || Opc == Instruction::Add) {
+ Type *Ty = X->getType();
+ if (Ty == Y->getType() && Ty->isIntOrIntVectorTy()) {
+ unsigned SrcBitWidth = Ty->getScalarSizeInBits();
+ unsigned DstBitWidth = I0->getType()->getScalarSizeInBits();
+ bool IsMul = Opc == Instruction::Mul;
+
+ if (C->isMask(SrcBitWidth) &&
+ DstBitWidth >= (IsMul ? 2 * SrcBitWidth : SrcBitWidth + 1)) {
+ // Check that all other users of the wide binop only need the
+ // low bits (trunc to <= SrcBitWidth, or and with low mask).
+ // This mirrors processUZExtIdiom's multi-use handling.
+ if (!IC.canReplaceWideOverflowIdiomUsers(I0, SrcBitWidth, &II))
+ return nullptr;
+
+ Value *Val, *Ov;
+ if (IsMul) {
+ Value *Ovf = Builder.CreateIntrinsic(Intrinsic::umul_with_overflow,
+ Ty, {X, Y},
+ /*FMFSource=*/nullptr, "umul");
+ Val = Builder.CreateExtractValue(Ovf, 0, "umul.value");
+ Ov = Builder.CreateExtractValue(Ovf, 1, "umul.overflow");
+ } else {
+ Val = Builder.CreateAdd(X, Y, "uadd");
+ Ov = Builder.CreateICmpULT(Val, X, "uadd.overflow");
+ }
+ Value *ZExtVal = Builder.CreateZExt(
+ Val, I0->getType(), IsMul ? "umul.zext" : "uadd.zext");
+
+ // Replace other users of the wide binop with the narrow result.
+ IC.replaceWideOverflowIdiomUsers(I0, SrcBitWidth, Val, &II);
+ return SelectInst::Create(Ov, I1, ZExtVal);
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
/// CallInst simplification. This mostly only handles folding of intrinsic
/// instructions. For normal calls, it allows visitCallBase to do the heavy
/// lifting.
@@ -2265,6 +2314,10 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
foldMinimumOverTrailingOrLeadingZeroCount<Intrinsic::ctlz>(
I0, I1, DL, Builder))
return replaceInstUsesWith(*II, FoldedCtlz);
+
+ if (Instruction *I = foldUMinSatOverflow(*II, I0, I1, Builder, *this))
+ return I;
+
[[fallthrough]];
}
case Intrinsic::umax: {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 270d3596ad679..2bdabdcc9eda6 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6826,34 +6826,8 @@ static Instruction *processUZExtIdiom(ICmpInst &I, Value *Val,
// In order to replace the original result with a narrower one, all uses must
// ignore upper bits of the result. The number of used low bits must be not
// greater than the width of add or mul.with.overflow.
- if (Val->hasNUsesOrMore(2))
- for (User *U : Val->users()) {
- if (U == &I)
- continue;
- if (TruncInst *TI = dyn_cast<TruncInst>(U)) {
- // Check if truncation ignores bits above ResultWidth.
- unsigned TruncWidth = TI->getType()->getPrimitiveSizeInBits();
- if (TruncWidth > ResultWidth)
- return nullptr;
- } else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(U)) {
- // Check if AND ignores bits above ResultWidth.
- if (BO->getOpcode() != Instruction::And)
- return nullptr;
- if (ConstantInt *CI = dyn_cast<ConstantInt>(BO->getOperand(1))) {
- const APInt &CVal = CI->getValue();
- if (CVal.getBitWidth() - CVal.countl_zero() > ResultWidth)
- return nullptr;
- } else {
- // In this case we could have the operand of the binary operation
- // being defined in another block, and performing the replacement
- // could break the dominance relation.
- return nullptr;
- }
- } else {
- // Other uses prohibit this transformation.
- return nullptr;
- }
- }
+ if (!IC.canReplaceWideOverflowIdiomUsers(Val, ResultWidth, &I))
+ return nullptr;
// Recognize patterns
switch (I.getPredicate()) {
@@ -6919,29 +6893,7 @@ static Instruction *processUZExtIdiom(ICmpInst &I, Value *Val,
IC.addToWorklist(Instr);
// Replace uses of the original add/mul result with the new arithmetic result
- if (Val->hasNUsesOrMore(2)) {
- for (User *U : make_early_inc_range(Val->users())) {
- if (U == &I)
- continue;
- if (TruncInst *TI = dyn_cast<TruncInst>(U)) {
- if (TI->getType()->getPrimitiveSizeInBits() == ResultWidth)
- IC.replaceInstUsesWith(*TI, ArithResult);
- else
- TI->setOperand(0, ArithResult);
- } else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(U)) {
- assert(BO->getOpcode() == Instruction::And);
- // Replace (ArithResult & mask) --> zext (ArithResult & short_mask)
- ConstantInt *CI = cast<ConstantInt>(BO->getOperand(1));
- APInt ShortMask = CI->getValue().trunc(ResultWidth);
- Value *ShortAnd = Builder.CreateAnd(ArithResult, ShortMask);
- Value *Zext = Builder.CreateZExt(ShortAnd, BO->getType());
- IC.replaceInstUsesWith(*BO, Zext);
- } else {
- llvm_unreachable("Unexpected Binary operation");
- }
- IC.addToWorklist(cast<Instruction>(U));
- }
- }
+ IC.replaceWideOverflowIdiomUsers(Val, ResultWidth, ArithResult, &I);
return IC.replaceInstUsesWith(I, OverflowCheck);
}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 8b759e701da60..eaa2da1255db5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -598,6 +598,14 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
const SimplifyQuery &Q,
unsigned Depth = 0);
+ /// Check if a wide add/mul has only users that demand the lower bits.
+ bool canReplaceWideOverflowIdiomUsers(Value *WideOp, unsigned NarrowWidth,
+ User *Exclude = nullptr);
+
+ /// Replace the valid users of WideOp with NarrowVal.
+ void replaceWideOverflowIdiomUsers(Value *WideOp, unsigned NarrowWidth,
+ Value *NarrowVal, User *Exclude = nullptr);
+
/// Helper routine of SimplifyDemandedUseBits. It tries to simplify demanded
/// bit for "r1 = shr x, c1; r2 = shl r1, c2" instruction sequence.
Value *simplifyShrShlDemandedBits(
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index b877f79a000cf..c30688feef317 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -6330,6 +6330,62 @@ bool InstructionCombiningPass::runOnFunction(Function &F) {
BFI, BPI, PSI, InstCombineOptions());
}
+bool InstCombinerImpl::canReplaceWideOverflowIdiomUsers(Value *WideOp,
+ unsigned NarrowWidth,
+ User *Exclude) {
+ if (WideOp->hasNUsesOrMore(2)) {
+ for (User *U : WideOp->users()) {
+ if (U == Exclude)
+ continue;
+ if (auto *TI = dyn_cast<TruncInst>(U)) {
+ if (TI->getType()->getScalarSizeInBits() > NarrowWidth)
+ return false;
+ } else if (auto *BO = dyn_cast<BinaryOperator>(U)) {
+ if (BO->getOpcode() != Instruction::And)
+ return false;
+ if (auto *CI = dyn_cast<ConstantInt>(BO->getOperand(1))) {
+ const APInt &CVal = CI->getValue();
+ if (CVal.getBitWidth() - CVal.countl_zero() > NarrowWidth)
+ return false;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void InstCombinerImpl::replaceWideOverflowIdiomUsers(Value *WideOp,
+ unsigned NarrowWidth,
+ Value *NarrowVal,
+ User *Exclude) {
+ if (WideOp->hasNUsesOrMore(2)) {
+ for (User *U : llvm::make_early_inc_range(WideOp->users())) {
+ if (U == Exclude)
+ continue;
+ if (auto *TI = dyn_cast<TruncInst>(U)) {
+ if (TI->getType()->getScalarSizeInBits() == NarrowWidth)
+ replaceInstUsesWith(*TI, NarrowVal);
+ else
+ TI->setOperand(0, NarrowVal);
+ addToWorklist(TI);
+ } else if (auto *BO = dyn_cast<BinaryOperator>(U)) {
+ assert(BO->getOpcode() == Instruction::And);
+ // Replace (WideOp & mask) --> zext (NarrowVal & short_mask)
+ ConstantInt *CI = cast<ConstantInt>(BO->getOperand(1));
+ APInt ShortMask = CI->getValue().trunc(NarrowWidth);
+ Value *ShortAnd = Builder.CreateAnd(NarrowVal, ShortMask);
+ Value *Zext = Builder.CreateZExt(ShortAnd, BO->getType());
+ replaceInstUsesWith(*BO, Zext);
+ addToWorklist(BO);
+ }
+ }
+ }
+}
+
char InstructionCombiningPass::ID = 0;
InstructionCombiningPass::InstructionCombiningPass() : FunctionPass(ID) {}
diff --git a/llvm/test/Transforms/InstCombine/umin-mul-overflow.ll b/llvm/test/Transforms/InstCombine/umin-mul-overflow.ll
index 938c04c63bbee..019ad5001964a 100644
--- a/llvm/test/Transforms/InstCombine/umin-mul-overflow.ll
+++ b/llvm/test/Transforms/InstCombine/umin-mul-overflow.ll
@@ -3,11 +3,10 @@
define i32 @test_mul_sat_32(i32 %a, i32 %b) {
; CHECK-LABEL: @test_mul_sat_32(
-; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
-; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
-; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[ZA]], [[ZB]]
-; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[MUL]], i64 4294967295)
-; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i64 [[UMIN]] to i32
+; CHECK-NEXT: [[UMUL:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[A:%.*]], i32 [[B:%.*]])
+; CHECK-NEXT: [[UMUL_VALUE:%.*]] = extractvalue { i32, i1 } [[UMUL]], 0
+; CHECK-NEXT: [[UMUL_OVERFLOW:%.*]] = extractvalue { i32, i1 } [[UMUL]], 1
+; CHECK-NEXT: [[TRUNC:%.*]] = select i1 [[UMUL_OVERFLOW]], i32 -1, i32 [[UMUL_VALUE]]
; CHECK-NEXT: ret i32 [[TRUNC]]
;
%za = zext i32 %a to i64
@@ -20,11 +19,10 @@ define i32 @test_mul_sat_32(i32 %a, i32 %b) {
define i64 @test_mul_sat_64(i64 %a, i64 %b) {
; CHECK-LABEL: @test_mul_sat_64(
-; CHECK-NEXT: [[ZA:%.*]] = zext i64 [[A:%.*]] to i128
-; CHECK-NEXT: [[ZB:%.*]] = zext i64 [[B:%.*]] to i128
-; CHECK-NEXT: [[MUL:%.*]] = mul nuw i128 [[ZA]], [[ZB]]
-; CHECK-NEXT: [[UMIN:%.*]] = call i128 @llvm.umin.i128(i128 [[MUL]], i128 18446744073709551615)
-; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i128 [[UMIN]] to i64
+; CHECK-NEXT: [[UMUL:%.*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[A:%.*]], i64 [[B:%.*]])
+; CHECK-NEXT: [[UMUL_VALUE:%.*]] = extractvalue { i64, i1 } [[UMUL]], 0
+; CHECK-NEXT: [[UMUL_OVERFLOW:%.*]] = extractvalue { i64, i1 } [[UMUL]], 1
+; CHECK-NEXT: [[TRUNC:%.*]] = select i1 [[UMUL_OVERFLOW]], i64 -1, i64 [[UMUL_VALUE]]
; CHECK-NEXT: ret i64 [[TRUNC]]
;
%za = zext i64 %a to i128
@@ -66,10 +64,9 @@ define i64 @test_add_sat_64(i64 %a, i64 %b) {
define i9 @test_add_nsw_poison(i8 %a, i8 %b) {
; CHECK-LABEL: @test_add_nsw_poison(
-; CHECK-NEXT: [[Y1:%.*]] = zext i8 [[B1:%.*]] to i9
-; CHECK-NEXT: [[Y:%.*]] = zext i8 [[B:%.*]] to i9
-; CHECK-NEXT: [[P:%.*]] = add nuw nsw i9 [[Y1]], [[Y]]
-; CHECK-NEXT: ret i9 [[P]]
+; CHECK-NEXT: [[B:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[A:%.*]], i8 [[B1:%.*]])
+; CHECK-NEXT: [[Y:%.*]] = zext i8 [[B]] to i9
+; CHECK-NEXT: ret i9 [[Y]]
;
%x = zext i8 %a to i9
%y = zext i8 %b to i9
@@ -123,14 +120,12 @@ define i64 @test_add_multiuse(i32 %a, i32 %b) {
; Positive test: mul has multi-use, but they are all truncs <= SrcBitWidth
define i32 @test_mul_multiuse_trunc(i32 %a, i32 %b) {
; CHECK-LABEL: @test_mul_multiuse_trunc(
-; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
-; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
-; CHECK-NEXT: [[MUL:%.*]] = mul nuw i64 [[ZA]], [[ZB]]
-; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[MUL]], i64 4294967295)
-; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i64 [[UMIN]] to i32
-; CHECK-NEXT: [[UMUL_VALUE:%.*]] = trunc i64 [[MUL]] to i32
+; CHECK-NEXT: [[UMUL:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[A:%.*]], i32 [[B:%.*]])
+; CHECK-NEXT: [[UMUL_VALUE:%.*]] = extractvalue { i32, i1 } [[UMUL]], 0
+; CHECK-NEXT: [[UMUL_OVERFLOW:%.*]] = extractvalue { i32, i1 } [[UMUL]], 1
+; CHECK-NEXT: [[TRUNC1:%.*]] = select i1 [[UMUL_OVERFLOW]], i32 -1, i32 [[UMUL_VALUE]]
; CHECK-NEXT: [[ZEXT:%.*]] = and i32 [[UMUL_VALUE]], 65535
-; CHECK-NEXT: [[RES:%.*]] = add i32 [[ZEXT]], [[TRUNC1]]
+; CHECK-NEXT: [[RES:%.*]] = add i32 [[TRUNC1]], [[ZEXT]]
; CHECK-NEXT: ret i32 [[RES]]
;
%za = zext i32 %a to i64
@@ -147,14 +142,10 @@ define i32 @test_mul_multiuse_trunc(i32 %a, i32 %b) {
; Positive test: add has multi-use, but they are all truncs <= SrcBitWidth
define i32 @test_add_multiuse_trunc(i32 %a, i32 %b) {
; CHECK-LABEL: @test_add_multiuse_trunc(
-; CHECK-NEXT: [[ZA:%.*]] = zext i32 [[A:%.*]] to i64
-; CHECK-NEXT: [[ZB:%.*]] = zext i32 [[B:%.*]] to i64
-; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[ZA]], [[ZB]]
-; CHECK-NEXT: [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 4294967295)
-; CHECK-NEXT: [[TRUNC1:%.*]] = trunc nuw i64 [[UMIN]] to i32
-; CHECK-NEXT: [[UADD:%.*]] = trunc i64 [[ADD]] to i32
+; CHECK-NEXT: [[UADD:%.*]] = add i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[TRUNC1:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[A]], i32 [[B]])
; CHECK-NEXT: [[ZEXT:%.*]] = and i32 [[UADD]], 65535
-; CHECK-NEXT: [[RES:%.*]] = add i32 [[ZEXT]], [[TRUNC1]]
+; CHECK-NEXT: [[RES:%.*]] = add i32 [[TRUNC1]], [[ZEXT]]
; CHECK-NEXT: ret i32 [[RES]]
;
%za = zext i32 %a to i64
More information about the llvm-commits
mailing list