[llvm] [ConstraintElim] Strengthen flags on mul and shl with constant operand. (PR #219293)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 28 03:06:01 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/219293
>From 0d923bc8af45e9d86e67d9b9dd4565a0ade80c6f Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 27 Aug 2026 18:37:17 +0100
Subject: [PATCH 1/3] [ConstraintElim] Add tests for shl/mul materialization
---
.../materialize-flags.ll | 186 +++++++++++++++++-
1 file changed, 185 insertions(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
index d27ff1b5e3068..862ad52176d25 100644
--- a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
+++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: none --version 6
; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
define i64 @sext_to_zext_nneg(i32 %n, i32 %m) {
@@ -455,3 +455,187 @@ bb.2:
exit:
ret ptr null
}
+
+; %x <u %n <u 1024, hence %x * 3 fits an i64 unsigned and signed.
+define i64 @mul_from_transitive_unsigned_bound(i64 %x, i64 %n) {
+; CHECK-LABEL: define i64 @mul_from_transitive_unsigned_bound(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp ult i64 [[N]], 1024
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[C_1:%.*]] = icmp ult i64 [[X]], [[N]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[MUL:%.*]] = mul i64 [[X]], 3
+; CHECK-NEXT: ret i64 [[MUL]]
+;
+entry:
+ %c.0 = icmp ult i64 %n, 1024
+ call void @llvm.assume(i1 %c.0)
+ %c.1 = icmp ult i64 %x, %n
+ call void @llvm.assume(i1 %c.1)
+ %mul = mul i64 %x, 3
+ ret i64 %mul
+}
+
+; Same as above, but there is no upper bound for %n.
+define i64 @mul_no_flags_unbounded_transitive_operand(i64 %x, i64 %n) {
+; CHECK-LABEL: define i64 @mul_no_flags_unbounded_transitive_operand(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_1:%.*]] = icmp ult i64 [[X]], [[N]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[MUL:%.*]] = mul i64 [[X]], 3
+; CHECK-NEXT: ret i64 [[MUL]]
+;
+entry:
+ %c.1 = icmp ult i64 %x, %n
+ call void @llvm.assume(i1 %c.1)
+ %mul = mul i64 %x, 3
+ ret i64 %mul
+}
+
+; %x <=u %x + %y <u 1024 due to the nuw add, hence %x << 3 fits an i64 unsigned.
+define i64 @shl_nuw_from_sum_bound(i64 %x, i64 %y) {
+; CHECK-LABEL: define i64 @shl_nuw_from_sum_bound(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[S:%.*]] = add nuw i64 [[X]], [[Y]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp ult i64 [[S]], 1024
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[SHL:%.*]] = shl i64 [[X]], 3
+; CHECK-NEXT: ret i64 [[SHL]]
+;
+entry:
+ %s = add nuw i64 %x, %y
+ %c.0 = icmp ult i64 %s, 1024
+ call void @llvm.assume(i1 %c.0)
+ %shl = shl i64 %x, 3
+ ret i64 %shl
+}
+
+; The bound on %x is one too weak for %x << 3 to fit an i64 unsigned.
+define i64 @shl_no_nuw_bound_too_weak(i64 %x) {
+; CHECK-LABEL: define i64 @shl_no_nuw_bound_too_weak(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp ult i64 [[X]], 2305843009213693953
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[SHL:%.*]] = shl i64 [[X]], 3
+; CHECK-NEXT: ret i64 [[SHL]]
+;
+entry:
+ %c.0 = icmp ult i64 %x, 2305843009213693953
+ call void @llvm.assume(i1 %c.0)
+ %shl = shl i64 %x, 3
+ ret i64 %shl
+}
+
+; %x >=s %z >=s 0 and %y >=s 0, hence the nsw product also fits unsigned.
+define i64 @mul_nuw_from_nsw_and_non_negative(i64 %x, i64 %y, i64 %z) {
+; CHECK-LABEL: define i64 @mul_nuw_from_nsw_and_non_negative(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[C_2:%.*]] = icmp sge i64 [[Y]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_2]])
+; CHECK-NEXT: [[MUL:%.*]] = mul nsw i64 [[X]], [[Y]]
+; CHECK-NEXT: ret i64 [[MUL]]
+;
+entry:
+ %c.0 = icmp sge i64 %x, %z
+ call void @llvm.assume(i1 %c.0)
+ %c.1 = icmp sge i64 %z, 0
+ call void @llvm.assume(i1 %c.1)
+ %c.2 = icmp sge i64 %y, 0
+ call void @llvm.assume(i1 %c.2)
+ %mul = mul nsw i64 %x, %y
+ ret i64 %mul
+}
+
+; Same as above, but %y may be negative.
+define i64 @mul_no_nuw_from_nsw_second_operand_may_be_negative(i64 %x, i64 %y, i64 %z) {
+; CHECK-LABEL: define i64 @mul_no_nuw_from_nsw_second_operand_may_be_negative(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[MUL:%.*]] = mul nsw i64 [[X]], [[Y]]
+; CHECK-NEXT: ret i64 [[MUL]]
+;
+entry:
+ %c.0 = icmp sge i64 %x, %z
+ call void @llvm.assume(i1 %c.0)
+ %c.1 = icmp sge i64 %z, 0
+ call void @llvm.assume(i1 %c.1)
+ %mul = mul nsw i64 %x, %y
+ ret i64 %mul
+}
+
+; %x >=s %z >=s 0, hence the nsw shift also fits unsigned, for any shift amount.
+define i64 @shl_nuw_from_nsw_and_non_negative(i64 %x, i64 %s, i64 %z) {
+; CHECK-LABEL: define i64 @shl_nuw_from_nsw_and_non_negative(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[S:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[SHL:%.*]] = shl nsw i64 [[X]], [[S]]
+; CHECK-NEXT: ret i64 [[SHL]]
+;
+entry:
+ %c.0 = icmp sge i64 %x, %z
+ call void @llvm.assume(i1 %c.0)
+ %c.1 = icmp sge i64 %z, 0
+ call void @llvm.assume(i1 %c.1)
+ %shl = shl nsw i64 %x, %s
+ ret i64 %shl
+}
+
+; Same as above, but the shift does not have nsw.
+define i64 @shl_no_nuw_non_negative_without_nsw(i64 %x, i64 %s, i64 %z) {
+; CHECK-LABEL: define i64 @shl_no_nuw_non_negative_without_nsw(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[S:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[SHL:%.*]] = shl i64 [[X]], [[S]]
+; CHECK-NEXT: ret i64 [[SHL]]
+;
+entry:
+ %c.0 = icmp sge i64 %x, %z
+ call void @llvm.assume(i1 %c.0)
+ %c.1 = icmp sge i64 %z, 0
+ call void @llvm.assume(i1 %c.1)
+ %shl = shl i64 %x, %s
+ ret i64 %shl
+}
+
+; -8 <=s %x <=s 7 implies %x * 16 does not wrap signed, but it may wrap
+; unsigned.
+define i8 @mul_nsw_only_from_signed_bounds(i8 %x) {
+; CHECK-LABEL: define i8 @mul_nsw_only_from_signed_bounds(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sle i8 [[X]], 7
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X]], -8
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X]], 16
+; CHECK-NEXT: ret i8 [[MUL]]
+;
+entry:
+ %c.0 = icmp sle i8 %x, 7
+ call void @llvm.assume(i1 %c.0)
+ %c.1 = icmp sge i8 %x, -8
+ call void @llvm.assume(i1 %c.1)
+ %mul = mul i8 %x, 16
+ ret i8 %mul
+}
>From e37d06bccb12b1ba186fc7e61969b7575d14aa7c Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 26 Aug 2026 09:57:01 +0100
Subject: [PATCH 2/3] [ConstraintElim] Strengthen flags on mul and shl with
constant operand.
Extend the flag strengthening to mul and shl if the second operand is a
constant. In that case ConstantRange::makeGuaranteedNoWrapRegion gives
the exact range for the first operand so the operation does not wrap.
Query the constraint system, to check if the first operand is within the
no-wrap region.
This enables a number of additional folds end-to-end:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1080
Note that in a few cases, the total number of instructions slightly
increases, mostly due to a few extra truncates.
Compile-time impact is in the noise:
https://llvm-compile-time-tracker.com/compare.php?from=7c9adcf3cd9bab7ebb739f7d10885d3433d6d7df&to=bd0d903e1335c5a5f6deb3d334cf2e9b46cc1b65&stat=instructions:u
---
.../Scalar/ConstraintElimination.cpp | 75 ++++++++++++++++++-
.../constraint-overflow.ll | 2 +-
.../geps-unsigned-predicates.ll | 18 ++---
.../materialize-flags.ll | 8 +-
.../Transforms/ConstraintElimination/shl.ll | 10 +--
5 files changed, 88 insertions(+), 25 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 56ff0a543ae93..52df61ae7a261 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1294,32 +1294,99 @@ static bool getConstraintFromMemoryAccess(GetElementPtrInst &GEP,
/// Returns true if \p I is a candidate whose poison-generating flags may be
/// strengthened using the constraint systems.
static bool canStrengthenFlags(Instruction *I) {
- switch (I->getOpcode()) {
+ auto *BO = dyn_cast<BinaryOperator>(I);
+ if (!BO || !BO->getType()->isIntegerTy())
+ return false;
+
+ switch (BO->getOpcode()) {
case Instruction::Sub:
// A - B does not wrap unsigned, if A >=u B. Subs with constant operands get
// canonicalized to Add.
- return I->getType()->isIntegerTy() && !I->hasNoUnsignedWrap() &&
- !isa<Constant>(I->getOperand(1));
+ return !BO->hasNoUnsignedWrap() && !isa<Constant>(BO->getOperand(1));
+ case Instruction::Mul:
+ case Instruction::Shl:
+ if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap())
+ return false;
+ // A constant second operand can be used to bound the first operand to
+ // refine no-wrap flags.
+ return isa<ConstantInt>(BO->getOperand(1));
default:
return false;
}
}
+/// Returns true if \p Info implies that \p Op is in \p R, interpreting \p R as
+/// a signed range if \p Signed is set and as an unsigned range otherwise.
+static bool doesHoldInRange(const ConstraintInfo &Info, Value *Op,
+ const ConstantRange &R, bool Signed) {
+ if (R.isFullSet() || R.isEmptySet() ||
+ (Signed ? R.isSignWrappedSet() : R.isWrappedSet()))
+ return false;
+
+ unsigned BitWidth = R.getBitWidth();
+ APInt Min = Signed ? R.getSignedMin() : R.getUnsignedMin();
+ APInt Max = Signed ? R.getSignedMax() : R.getUnsignedMax();
+ APInt MinVal = Signed ? APInt::getSignedMinValue(BitWidth)
+ : APInt::getMinValue(BitWidth);
+ APInt MaxVal = Signed ? APInt::getSignedMaxValue(BitWidth)
+ : APInt::getMaxValue(BitWidth);
+ Type *Ty = Op->getType();
+ if (Min != MinVal &&
+ !Info.doesHold(Signed ? CmpInst::ICMP_SGE : CmpInst::ICMP_UGE, Op,
+ ConstantInt::get(Ty, Min)))
+ return false;
+ if (Max != MaxVal &&
+ !Info.doesHold(Signed ? CmpInst::ICMP_SLE : CmpInst::ICMP_ULE, Op,
+ ConstantInt::get(Ty, Max)))
+ return false;
+ return true;
+}
+
/// Try to strengthen \p I's poison generating flags using \p Info. Returns
/// true if \p I was modified.
static bool tryToStrengthenFlags(Instruction *I, ConstraintInfo &Info,
SmallVectorImpl<Instruction *> &ToRemove) {
assert(canStrengthenFlags(I) && "not a candidate for flag strengthening");
+ using OBO = OverflowingBinaryOperator;
+ Value *Op0 = I->getOperand(0), *Op1 = I->getOperand(1);
switch (I->getOpcode()) {
case Instruction::Sub: {
// Op0 - Op1 does not wrap unsigned, if Op0 >=u Op1.
- if (!Info.doesHold(CmpInst::ICMP_UGE, I->getOperand(0), I->getOperand(1)))
+ if (!Info.doesHold(CmpInst::ICMP_UGE, Op0, Op1))
return false;
LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
I->setHasNoUnsignedWrap();
return true;
}
+ case Instruction::Mul:
+ case Instruction::Shl: {
+ auto Opcode = static_cast<Instruction::BinaryOps>(I->getOpcode());
+ bool Changed = false;
+ // For a constant Op1, the ranges of Op0 for which the operation does not
+ // wrap are known exactly; check if the systems imply one of them.
+ auto *C = cast<ConstantInt>(Op1);
+ ConstantRange Other(C->getValue());
+ if (!I->hasNoUnsignedWrap() &&
+ doesHoldInRange(Info, Op0,
+ ConstantRange::makeGuaranteedNoWrapRegion(
+ Opcode, Other, OBO::NoUnsignedWrap),
+ /*Signed=*/false)) {
+ LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
+ I->setHasNoUnsignedWrap();
+ Changed = true;
+ }
+ if (!I->hasNoSignedWrap() &&
+ doesHoldInRange(Info, Op0,
+ ConstantRange::makeGuaranteedNoWrapRegion(
+ Opcode, Other, OBO::NoSignedWrap),
+ /*Signed=*/true)) {
+ LLVM_DEBUG(dbgs() << "Adding nsw to " << *I << "\n");
+ I->setHasNoSignedWrap();
+ Changed = true;
+ }
+ return Changed;
+ }
default:
return false;
}
diff --git a/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll b/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll
index bf2e4a933cf6d..41756fd4b16e3 100644
--- a/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll
+++ b/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll
@@ -88,7 +88,7 @@ define i1 @fm_overflow_recovery(i64 %n, i64 %i, i64 %lim) {
; CHECK-NEXT: [[N4M4:%.*]] = add i64 [[N4]], -4
; CHECK-NEXT: [[F2:%.*]] = icmp ult i64 [[N4M4]], [[LIM]]
; CHECK-NEXT: call void @llvm.assume(i1 [[F2]])
-; CHECK-NEXT: [[I4:%.*]] = shl nuw i64 [[I]], 2
+; CHECK-NEXT: [[I4:%.*]] = shl nuw nsw i64 [[I]], 2
; CHECK-NEXT: ret i1 true
;
entry:
diff --git a/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll b/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll
index 612bf2f0a8337..a7210b3a80121 100644
--- a/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll
+++ b/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll
@@ -490,15 +490,14 @@ define void @test.ult.gep.shl(ptr readonly %src, ptr readnone %max, i8 %idx) {
; CHECK-NEXT: [[IDX_SHL_1:%.*]] = shl nuw nsw i8 [[IDX]], 1
; CHECK-NEXT: [[ADD_PTR_SHL_1:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_1]]
; CHECK-NEXT: call void @use(i1 true)
-; CHECK-NEXT: [[IDX_SHL_2:%.*]] = shl nuw i8 [[IDX]], 2
+; CHECK-NEXT: [[IDX_SHL_2:%.*]] = shl nuw nsw i8 [[IDX]], 2
; CHECK-NEXT: [[ADD_PTR_SHL_2:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_2]]
; CHECK-NEXT: [[C_MAX_1:%.*]] = icmp ult ptr [[ADD_PTR_SHL_2]], [[MAX]]
; CHECK-NEXT: call void @use(i1 [[C_MAX_1]])
-; CHECK-NEXT: [[IDX_SHL_NOT_NUW:%.*]] = shl i8 [[IDX]], 1
+; CHECK-NEXT: [[IDX_SHL_NOT_NUW:%.*]] = shl nuw nsw i8 [[IDX]], 1
; CHECK-NEXT: [[ADD_PTR_SHL_NOT_NUW:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_NOT_NUW]]
-; CHECK-NEXT: [[C_MAX_2:%.*]] = icmp ult ptr [[ADD_PTR_SHL_NOT_NUW]], [[MAX]]
-; CHECK-NEXT: call void @use(i1 [[C_MAX_2]])
-; CHECK-NEXT: [[IDX_SHL_3:%.*]] = shl nuw i8 [[IDX]], 3
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: [[IDX_SHL_3:%.*]] = shl nuw nsw i8 [[IDX]], 3
; CHECK-NEXT: [[ADD_PTR_SHL_3:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_3]]
; CHECK-NEXT: [[C_MAX_3:%.*]] = icmp ult ptr [[ADD_PTR_SHL_3]], [[MAX]]
; CHECK-NEXT: call void @use(i1 [[C_MAX_3]])
@@ -549,16 +548,15 @@ define void @test.ult.gep.shl.zext(ptr readonly %src, ptr readnone %max, i32 %id
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[IDX:%.*]], 5
; CHECK-NEXT: br i1 [[CMP]], label [[CHECK_MAX:%.*]], label [[TRAP]]
; CHECK: check.max:
-; CHECK-NEXT: [[IDX_SHL:%.*]] = shl nuw i32 [[IDX]], 1
+; CHECK-NEXT: [[IDX_SHL:%.*]] = shl nuw nsw i32 [[IDX]], 1
; CHECK-NEXT: [[EXT_1:%.*]] = zext i32 [[IDX_SHL]] to i64
; CHECK-NEXT: [[ADD_PTR_SHL:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[EXT_1]]
; CHECK-NEXT: call void @use(i1 true)
-; CHECK-NEXT: [[IDX_SHL_NOT_NUW:%.*]] = shl i32 [[IDX]], 1
+; CHECK-NEXT: [[IDX_SHL_NOT_NUW:%.*]] = shl nuw nsw i32 [[IDX]], 1
; CHECK-NEXT: [[EXT_2:%.*]] = zext i32 [[IDX_SHL_NOT_NUW]] to i64
; CHECK-NEXT: [[ADD_PTR_SHL_NOT_NUW:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[EXT_2]]
-; CHECK-NEXT: [[C_MAX_1:%.*]] = icmp ult ptr [[ADD_PTR_SHL_NOT_NUW]], [[MAX]]
-; CHECK-NEXT: call void @use(i1 [[C_MAX_1]])
-; CHECK-NEXT: [[IDX_SHL_3:%.*]] = shl nuw i32 [[IDX]], 2
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: [[IDX_SHL_3:%.*]] = shl nuw nsw i32 [[IDX]], 2
; CHECK-NEXT: [[EXT_3:%.*]] = zext i32 [[IDX_SHL_3]] to i64
; CHECK-NEXT: [[ADD_PTR_SHL_3:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[EXT_3]]
; CHECK-NEXT: [[C_MAX_2:%.*]] = icmp ult ptr [[ADD_PTR_SHL_3]], [[MAX]]
diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
index 862ad52176d25..2ec78db674b04 100644
--- a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
+++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: none --version 6
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
define i64 @sext_to_zext_nneg(i32 %n, i32 %m) {
@@ -465,7 +465,7 @@ define i64 @mul_from_transitive_unsigned_bound(i64 %x, i64 %n) {
; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
; CHECK-NEXT: [[C_1:%.*]] = icmp ult i64 [[X]], [[N]]
; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
-; CHECK-NEXT: [[MUL:%.*]] = mul i64 [[X]], 3
+; CHECK-NEXT: [[MUL:%.*]] = mul nuw nsw i64 [[X]], 3
; CHECK-NEXT: ret i64 [[MUL]]
;
entry:
@@ -502,7 +502,7 @@ define i64 @shl_nuw_from_sum_bound(i64 %x, i64 %y) {
; CHECK-NEXT: [[S:%.*]] = add nuw i64 [[X]], [[Y]]
; CHECK-NEXT: [[C_0:%.*]] = icmp ult i64 [[S]], 1024
; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
-; CHECK-NEXT: [[SHL:%.*]] = shl i64 [[X]], 3
+; CHECK-NEXT: [[SHL:%.*]] = shl nuw i64 [[X]], 3
; CHECK-NEXT: ret i64 [[SHL]]
;
entry:
@@ -628,7 +628,7 @@ define i8 @mul_nsw_only_from_signed_bounds(i8 %x) {
; CHECK-NEXT: call void @llvm.assume(i1 [[C_0]])
; CHECK-NEXT: [[C_1:%.*]] = icmp sge i8 [[X]], -8
; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
-; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X]], 16
+; CHECK-NEXT: [[MUL:%.*]] = mul nsw i8 [[X]], 16
; CHECK-NEXT: ret i8 [[MUL]]
;
entry:
diff --git a/llvm/test/Transforms/ConstraintElimination/shl.ll b/llvm/test/Transforms/ConstraintElimination/shl.ll
index 3affdd2a5291f..a6d5ff396e94e 100644
--- a/llvm/test/Transforms/ConstraintElimination/shl.ll
+++ b/llvm/test/Transforms/ConstraintElimination/shl.ll
@@ -1399,9 +1399,8 @@ define i1 @shl_nuw_signed_bounded(i64 %c) {
; CHECK-NEXT: call void @llvm.assume(i1 [[BND]])
; CHECK-NEXT: [[NN:%.*]] = icmp sge i64 [[C]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NN]])
-; CHECK-NEXT: [[M:%.*]] = shl nuw i64 [[C]], 2
-; CHECK-NEXT: [[T:%.*]] = icmp slt i64 [[M]], 0
-; CHECK-NEXT: ret i1 [[T]]
+; CHECK-NEXT: [[M:%.*]] = shl nuw nsw i64 [[C]], 2
+; CHECK-NEXT: ret i1 false
;
entry:
%bnd = icmp slt i64 %c, 2305843009213693952
@@ -1419,9 +1418,8 @@ define i1 @shl_nuw_signed_tight_bound(i8 %x) {
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
; CHECK-NEXT: [[NN:%.*]] = icmp sge i8 [[X]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NN]])
-; CHECK-NEXT: [[M:%.*]] = shl nuw i8 [[X]], 2
-; CHECK-NEXT: [[T:%.*]] = icmp slt i8 [[M]], 0
-; CHECK-NEXT: ret i1 [[T]]
+; CHECK-NEXT: [[M:%.*]] = shl nuw nsw i8 [[X]], 2
+; CHECK-NEXT: ret i1 false
;
%b = icmp slt i8 %x, 32
call void @llvm.assume(i1 %b)
>From 6769ef343e0dc2d25ac77b6dd90bd1a2359d4332 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 28 Aug 2026 11:01:37 +0100
Subject: [PATCH 3/3] !fixup dont bail out on full set
---
.../Scalar/ConstraintElimination.cpp | 6 ++--
.../large-constant-ints.ll | 8 ++---
.../ConstraintElimination/mul-nsw.ll | 30 +++++++++----------
.../Transforms/ConstraintElimination/shl.ll | 6 ++--
4 files changed, 26 insertions(+), 24 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 52df61ae7a261..65cfcd78205f6 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1319,10 +1319,12 @@ static bool canStrengthenFlags(Instruction *I) {
/// a signed range if \p Signed is set and as an unsigned range otherwise.
static bool doesHoldInRange(const ConstraintInfo &Info, Value *Op,
const ConstantRange &R, bool Signed) {
- if (R.isFullSet() || R.isEmptySet() ||
- (Signed ? R.isSignWrappedSet() : R.isWrappedSet()))
+ if (R.isEmptySet() || (Signed ? R.isSignWrappedSet() : R.isWrappedSet()))
return false;
+ if (R.isFullSet())
+ return true;
+
unsigned BitWidth = R.getBitWidth();
APInt Min = Signed ? R.getSignedMin() : R.getUnsignedMin();
APInt Max = Signed ? R.getSignedMax() : R.getUnsignedMax();
diff --git a/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll b/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll
index a80e492e08246..8ee55f98f0e36 100644
--- a/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll
+++ b/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll
@@ -152,13 +152,13 @@ else:
define i1 @gep_zext_shl_decomp_i80(ptr %a, i80 %v) {
; CHECK-LABEL: @gep_zext_shl_decomp_i80(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[SHL:%.*]] = shl nuw i80 [[V:%.*]], 1973801615886922022913
+; CHECK-NEXT: [[SHL:%.*]] = shl nuw nsw i80 [[V:%.*]], 1973801615886922022913
; CHECK-NEXT: [[EXT:%.*]] = zext i80 [[SHL]] to i128
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[A:%.*]], i128 [[EXT]]
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[GEP]], null
; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
-; CHECK-NEXT: [[SHL_1:%.*]] = shl nuw i80 [[V]], 1973801615886922022913
+; CHECK-NEXT: [[SHL_1:%.*]] = shl nuw nsw i80 [[V]], 1973801615886922022913
; CHECK-NEXT: [[EXT_1:%.*]] = zext i80 [[SHL_1]] to i128
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i8, ptr [[A]], i128 [[EXT_1]]
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[GEP_1]], null
@@ -222,12 +222,12 @@ else:
define i1 @gep_shl_decomp_i80(ptr %a, i80 %v) {
; CHECK-LABEL: @gep_shl_decomp_i80(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[SHL:%.*]] = shl nuw i80 [[V:%.*]], 1973801615886922022913
+; CHECK-NEXT: [[SHL:%.*]] = shl nuw nsw i80 [[V:%.*]], 1973801615886922022913
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[A:%.*]], i80 [[SHL]]
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[GEP]], null
; CHECK-NEXT: br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
-; CHECK-NEXT: [[SHL_1:%.*]] = shl nuw i80 [[V]], 1973801615886922022913
+; CHECK-NEXT: [[SHL_1:%.*]] = shl nuw nsw i80 [[V]], 1973801615886922022913
; CHECK-NEXT: [[GEP_1:%.*]] = getelementptr inbounds i8, ptr [[A]], i80 [[SHL_1]]
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[GEP_1]], null
; CHECK-NEXT: ret i1 [[C_1]]
diff --git a/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll b/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
index f2f6a91ebc9e8..4bae51d2fcb04 100644
--- a/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
@@ -14,7 +14,7 @@ define void @slt_mul_nsw_3_known_positive_1(i8 %start, i8 %high) {
; CHECK-NEXT: br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: call void @use(i1 true)
-; CHECK-NEXT: [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_2:%.*]] = mul nsw i8 [[START]], 2
; CHECK-NEXT: call void @use(i1 true)
@@ -68,7 +68,7 @@ define void @slt_mul_nsw_3_known_positive_2(i8 %start, i8 %high) {
; CHECK: else:
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -121,7 +121,7 @@ define void @slt_mul_no_nsw_3_known_positive_1(i8 %start, i8 %high) {
; CHECK: then:
; CHECK-NEXT: [[C_3:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_3]])
-; CHECK-NEXT: [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -178,7 +178,7 @@ define void @slt_mul_no_nsw_3_known_positive_2(i8 %start, i8 %high) {
; CHECK: else:
; CHECK-NEXT: [[C_8:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_8]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_9:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_9]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -229,7 +229,7 @@ define void @slt_mul_nsw_3_not_known_positive_1(i8 %start, i8 %high) {
; CHECK: then:
; CHECK-NEXT: [[C_3:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_3]])
-; CHECK-NEXT: [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -282,7 +282,7 @@ define void @slt_mul_nsw_3_not_known_positive_2(i8 %start, i8 %high) {
; CHECK: else:
; CHECK-NEXT: [[C_8:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_8]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_9:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_9]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -439,7 +439,7 @@ define void @slt_mul_nsw_3_known_negative_1(i8 %start, i8 %high) {
; CHECK: then:
; CHECK-NEXT: [[C_3:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_3]])
-; CHECK-NEXT: [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -494,7 +494,7 @@ define void @slt_mul_nsw_3_known_negative_2(i8 %start, i8 %high) {
; CHECK: else:
; CHECK-NEXT: [[C_6:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_6]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_7:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_7]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -654,7 +654,7 @@ define void @slt_mul_nsw_3_known_nonnegative_1(i8 %start, i8 %high) {
; CHECK-NEXT: br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: call void @use(i1 true)
-; CHECK-NEXT: [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_2:%.*]] = mul nsw i8 [[START]], 2
; CHECK-NEXT: call void @use(i1 true)
@@ -708,7 +708,7 @@ define void @slt_mul_nsw_3_known_nonnegative_2(i8 %start, i8 %high) {
; CHECK: else:
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -765,7 +765,7 @@ define void @slt_mul_nsw_both_var_non_negative_1(i8 %start, i8 %scale, i8 %high)
; CHECK: then:
; CHECK-NEXT: [[T_0:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_0]])
-; CHECK-NEXT: [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[T_1:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_1]])
; CHECK-NEXT: [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -781,7 +781,7 @@ define void @slt_mul_nsw_both_var_non_negative_1(i8 %start, i8 %scale, i8 %high)
; CHECK: else:
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -858,7 +858,7 @@ define void @slt_mul_nsw_both_var_non_negative_2(i8 %start, i8 %scale, i8 %high)
; CHECK: else:
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -915,7 +915,7 @@ define void @ult_mul_nsw_3_known_positive_1(i8 %start, i8 %high) {
; CHECK: then:
; CHECK-NEXT: [[T_0:%.*]] = icmp ult i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_0]])
-; CHECK-NEXT: [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[T_1:%.*]] = icmp ult i8 [[START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_1]])
; CHECK-NEXT: [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -972,7 +972,7 @@ define void @ult_mul_nsw_3_known_positive_2(i8 %start, i8 %high) {
; CHECK: else:
; CHECK-NEXT: [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
-; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT: [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
; CHECK-NEXT: [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
diff --git a/llvm/test/Transforms/ConstraintElimination/shl.ll b/llvm/test/Transforms/ConstraintElimination/shl.ll
index a6d5ff396e94e..a09af84a64b56 100644
--- a/llvm/test/Transforms/ConstraintElimination/shl.ll
+++ b/llvm/test/Transforms/ConstraintElimination/shl.ll
@@ -1360,7 +1360,7 @@ define i1 @shl_nsw_by_bw_minus_1(i64 %x) {
; Shift returns poison in this case, just make sure we don't crash.
define i1 @shl_nsw_by_bw(i64 %x) {
; CHECK-LABEL: @shl_nsw_by_bw(
-; CHECK-NEXT: [[X_SHL:%.*]] = shl nsw i64 [[X:%.*]], 64
+; CHECK-NEXT: [[X_SHL:%.*]] = shl nuw nsw i64 [[X:%.*]], 64
; CHECK-NEXT: [[C_1:%.*]] = icmp slt i64 [[X_SHL]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
; CHECK-NEXT: [[T_1:%.*]] = icmp slt i64 [[X]], 0
@@ -1377,7 +1377,7 @@ define i1 @shl_nsw_by_bw(i64 %x) {
; Shift returns poison in this case, just make sure we don't crash.
define i1 @shl_nsw_by_bw_plus_1(i64 %x) {
; CHECK-LABEL: @shl_nsw_by_bw_plus_1(
-; CHECK-NEXT: [[X_SHL:%.*]] = shl nsw i64 [[X:%.*]], 65
+; CHECK-NEXT: [[X_SHL:%.*]] = shl nuw nsw i64 [[X:%.*]], 65
; CHECK-NEXT: [[C_1:%.*]] = icmp slt i64 [[X_SHL]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
; CHECK-NEXT: [[T_1:%.*]] = icmp slt i64 [[X]], 0
@@ -1464,7 +1464,7 @@ define i1 @shl_nuw_signed_shift_zero(i8 %x) {
; CHECK-LABEL: @shl_nuw_signed_shift_zero(
; CHECK-NEXT: [[B:%.*]] = icmp slt i8 [[X:%.*]], 32
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT: [[M:%.*]] = shl nuw i8 [[X]], 0
+; CHECK-NEXT: [[M:%.*]] = shl nuw nsw i8 [[X]], 0
; CHECK-NEXT: [[T:%.*]] = icmp slt i8 [[M]], 0
; CHECK-NEXT: ret i1 [[T]]
;
More information about the llvm-commits
mailing list