[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