[llvm] [ConstraintElim] Avoid int64_t{1} << 63 when decomposing SHL. (PR #215058)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 8 22:39:08 PDT 2026
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/215058
int64_t{1} << 63 is defined as INT64_MIN on C++20 and later. In C++17
and earlier, most implementations also already implement this as
INT64_MIN, and UBSan in Clang does not flag it as UB.
Bail out if the shift amount is >= 63, as we would incorrectly add a
negative coefficient to an unsigned constraint.
>From a0b65a516004c88dbdf665b63d570b08ce7dda26 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sat, 8 Aug 2026 06:06:08 +0100
Subject: [PATCH 1/2] [ConstraintElim] Add test.
---
.../Transforms/ConstraintElimination/shl.ll | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/llvm/test/Transforms/ConstraintElimination/shl.ll b/llvm/test/Transforms/ConstraintElimination/shl.ll
index bff8c7e037dee..e7263fe17e381 100644
--- a/llvm/test/Transforms/ConstraintElimination/shl.ll
+++ b/llvm/test/Transforms/ConstraintElimination/shl.ll
@@ -1478,3 +1478,55 @@ define i1 @shl_nuw_signed_shift_zero(i8 %x) {
%t = icmp slt i8 %m, 0
ret i1 %t
}
+
+define i1 @shl_nuw_by_62(i64 %start, i64 %high) {
+; CHECK-LABEL: @shl_nuw_by_62(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[START_SHL_62:%.*]] = shl nuw i64 [[START:%.*]], 62
+; CHECK-NEXT: [[C_1:%.*]] = icmp ult i64 [[START_SHL_62]], [[HIGH:%.*]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: ret i1 true
+;
+entry:
+ %start.shl.62 = shl nuw i64 %start, 62
+ %c.1 = icmp ult i64 %start.shl.62, %high
+ call void @llvm.assume(i1 %c.1)
+ %t.1 = icmp ult i64 %start, %high
+ ret i1 %t.1
+}
+
+; A shift of 63 would require a scale of 2^63, which is not representable in
+; the signed i64 coefficient
+define i1 @shl_nuw_by_63(i64 %start, i64 %high) {
+; CHECK-LABEL: @shl_nuw_by_63(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[START_SHL_63:%.*]] = shl nuw i64 [[START:%.*]], 63
+; CHECK-NEXT: [[C_1:%.*]] = icmp ult i64 [[START_SHL_63]], [[HIGH:%.*]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT: [[T_1:%.*]] = icmp ult i64 [[START]], [[HIGH]]
+; CHECK-NEXT: ret i1 [[T_1]]
+;
+entry:
+ %start.shl.63 = shl nuw i64 %start, 63
+ %c.1 = icmp ult i64 %start.shl.63, %high
+ call void @llvm.assume(i1 %c.1)
+ %t.1 = icmp ult i64 %start, %high
+ ret i1 %t.1
+}
+
+define i1 @shl_nuw_by_63_self(i64 %x, i64 %b) {
+; CHECK-LABEL: @shl_nuw_by_63_self(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[S:%.*]] = shl nuw i64 [[X:%.*]], 63
+; CHECK-NEXT: [[C:%.*]] = icmp ule i64 [[S]], [[B:%.*]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[C2:%.*]] = icmp ule i64 [[S]], [[B]]
+; CHECK-NEXT: ret i1 [[C2]]
+;
+entry:
+ %s = shl nuw i64 %x, 63
+ %c = icmp ule i64 %s, %b
+ call void @llvm.assume(i1 %c)
+ %c2 = icmp ule i64 %s, %b
+ ret i1 %c2
+}
>From 744e4ea7bd418ef3020821b907e36bad2a93afbe Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sat, 8 Aug 2026 06:06:26 +0100
Subject: [PATCH 2/2] [ConstraintElim] Avoid int64_t{1} << 63 when decomposing
SHL.
int64_t{1} << 63 is defined as INT64_MIN on C++20 and later. In C++17
and earlier, most implementations also already implement this as
INT64_MIN, and UBSan in Clang does not flag it as UB.
Bail out if the shift amount is >= 63, as we would incorrectly add a
negative coefficient to an unsigned constraint.
---
llvm/lib/Transforms/Scalar/ConstraintElimination.cpp | 4 +++-
llvm/test/Transforms/ConstraintElimination/shl.ll | 3 +--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 44516103fd4e1..b31ae075e8f4a 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -664,7 +664,9 @@ static Decomposition decompose(Value *V, const ConstraintInfo &Info,
}
if (match(V, m_NUWShl(m_Value(Op1), m_ConstantInt(CI))) && canUseSExt(CI)) {
- if (CI->getSExtValue() < 0 || CI->getSExtValue() >= 64)
+ // The scale 1 << shift must fit in the signed coefficient, so reject a
+ // shift of 63, for which int64_t{1} << 63 is INT64_MIN.
+ if (CI->getSExtValue() < 0 || CI->getSExtValue() >= 63)
return V;
auto Result = decompose(Op1, Info, IsSigned, DL);
if (!Result.mul(int64_t{1} << CI->getSExtValue()))
diff --git a/llvm/test/Transforms/ConstraintElimination/shl.ll b/llvm/test/Transforms/ConstraintElimination/shl.ll
index e7263fe17e381..fe053120ac871 100644
--- a/llvm/test/Transforms/ConstraintElimination/shl.ll
+++ b/llvm/test/Transforms/ConstraintElimination/shl.ll
@@ -1520,8 +1520,7 @@ define i1 @shl_nuw_by_63_self(i64 %x, i64 %b) {
; CHECK-NEXT: [[S:%.*]] = shl nuw i64 [[X:%.*]], 63
; CHECK-NEXT: [[C:%.*]] = icmp ule i64 [[S]], [[B:%.*]]
; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT: [[C2:%.*]] = icmp ule i64 [[S]], [[B]]
-; CHECK-NEXT: ret i1 [[C2]]
+; CHECK-NEXT: ret i1 true
;
entry:
%s = shl nuw i64 %x, 63
More information about the llvm-commits
mailing list