[llvm] [GlobalISel] Add G_ROTL/G_ROTR to computeNumSignBits (PR #213364)
John Favret via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 2 15:02:42 PDT 2026
https://github.com/johnfav03 updated https://github.com/llvm/llvm-project/pull/213364
>From 8a860c199c64fa0af7003a3a57bbd03c8252779c Mon Sep 17 00:00:00 2001
From: johnfav03 <64748847+johnfav03 at users.noreply.github.com>
Date: Fri, 31 Jul 2026 16:27:10 -0500
Subject: [PATCH 1/2] [GlobalISel] Add G_ROTL/G_ROTR to computeNumSignBits
---
.../CodeGen/GlobalISel/GISelValueTracking.cpp | 24 +++++++
.../GlobalISel/knownbits-rotl-rotr.mir | 68 ++++++++++++++++++-
2 files changed, 91 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 7ff23d8e1bd6e..a1188dd2468c3 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -2370,6 +2370,30 @@ unsigned GISelValueTracking::computeNumSignBits(Register R,
}
break;
}
+ case TargetOpcode::G_ROTL:
+ case TargetOpcode::G_ROTR: {
+ Register SrcReg = MI.getOperand(1).getReg();
+ unsigned Tmp = computeNumSignBits(SrcReg, DemandedElts, Depth + 1);
+
+ // If we're rotating an 0/-1 value, then it stays an 0/-1 value.
+ if (Tmp == TyBits)
+ return TyBits;
+
+ if (auto MaybeAmt =
+ isConstantOrConstantSplatVector(MI.getOperand(2).getReg(), MRI)) {
+ unsigned RotAmt = MaybeAmt->urem(TyBits);
+
+ // Handle rotate right by N like a rotate left by TyBits-N.
+ if (Opcode == TargetOpcode::G_ROTR)
+ RotAmt = (TyBits - RotAmt) % TyBits;
+
+ // If we aren't rotating out all of the known-in sign bits, return the
+ // number that are left. This handles rotl(sext(x), 1) for example.
+ if (Tmp > RotAmt + 1)
+ FirstAnswer = Tmp - RotAmt;
+ }
+ break;
+ }
case TargetOpcode::G_SREM: {
// The sign bit is the LHS's sign bit, except when the result of the
// remainder is zero. The magnitude of the result should be less than or
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-rotl-rotr.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-rotl-rotr.mir
index 004484ff1f5ac..74d2b0ac0eb36 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-rotl-rotr.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-rotl-rotr.mir
@@ -232,13 +232,55 @@ body: |
; CHECK-NEXT: %0:_ KnownBits:???????? SignBits:1
; CHECK-NEXT: %1:_ KnownBits:???????????????? SignBits:9
; CHECK-NEXT: %2:_ KnownBits:0000000000001101 SignBits:12
- ; CHECK-NEXT: %3:_ KnownBits:???????????????? SignBits:1
+ ; CHECK-NEXT: %3:_ KnownBits:???????????????? SignBits:6
%0:_(i8) = COPY $b0
%1:_(i16) = G_SEXT %0(i8)
%2:_(i16) = G_CONSTANT i16 13
%3:_(i16) = G_ROTR %1, %2
...
---
+name: ROTLless
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: @ROTLless
+ ; CHECK-NEXT: %0:_ KnownBits:???????? SignBits:1
+ ; CHECK-NEXT: %1:_ KnownBits:???????????????? SignBits:9
+ ; CHECK-NEXT: %2:_ KnownBits:0000000000001101 SignBits:12
+ ; CHECK-NEXT: %3:_ KnownBits:???????????????? SignBits:1
+ %0:_(i8) = COPY $b0
+ %1:_(i16) = G_SEXT %0(i8)
+ %2:_(i16) = G_CONSTANT i16 13
+ %3:_(i16) = G_ROTL %1, %2
+...
+---
+name: ROTLeq
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: @ROTLeq
+ ; CHECK-NEXT: %0:_ KnownBits:???????? SignBits:1
+ ; CHECK-NEXT: %1:_ KnownBits:???????????????? SignBits:9
+ ; CHECK-NEXT: %2:_ KnownBits:0000000000001000 SignBits:12
+ ; CHECK-NEXT: %3:_ KnownBits:???????????????? SignBits:1
+ %0:_(i8) = COPY $b0
+ %1:_(i16) = G_SEXT %0(i8)
+ %2:_(i16) = G_CONSTANT i16 8
+ %3:_(i16) = G_ROTL %1, %2
+...
+---
+name: ROTLmore
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: @ROTLmore
+ ; CHECK-NEXT: %0:_ KnownBits:???????? SignBits:1
+ ; CHECK-NEXT: %1:_ KnownBits:???????????????? SignBits:9
+ ; CHECK-NEXT: %2:_ KnownBits:0000000000000011 SignBits:14
+ ; CHECK-NEXT: %3:_ KnownBits:???????????????? SignBits:6
+ %0:_(i8) = COPY $b0
+ %1:_(i16) = G_SEXT %0(i8)
+ %2:_(i16) = G_CONSTANT i16 3
+ %3:_(i16) = G_ROTL %1, %2
+...
+---
name: SignBitsThroughZext
body: |
bb.1:
@@ -268,3 +310,27 @@ body: |
%1:_(i8) = G_CONSTANT i8 2
%2:_(i8) = G_ROTL %0, %1
...
+---
+name: RotateAllOnesUnknownAmtROTR
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: @RotateAllOnesUnknownAmtROTR
+ ; CHECK-NEXT: %0:_ KnownBits:11111111 SignBits:8
+ ; CHECK-NEXT: %1:_ KnownBits:???????? SignBits:1
+ ; CHECK-NEXT: %2:_ KnownBits:???????? SignBits:8
+ %0:_(i8) = G_CONSTANT i8 255
+ %1:_(i8) = COPY $b0
+ %2:_(i8) = G_ROTR %0, %1
+...
+---
+name: RotateAllZerosUnknownAmtROTL
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: @RotateAllZerosUnknownAmtROTL
+ ; CHECK-NEXT: %0:_ KnownBits:00000000 SignBits:8
+ ; CHECK-NEXT: %1:_ KnownBits:???????? SignBits:1
+ ; CHECK-NEXT: %2:_ KnownBits:???????? SignBits:8
+ %0:_(i8) = G_CONSTANT i8 0
+ %1:_(i8) = COPY $b0
+ %2:_(i8) = G_ROTL %0, %1
+...
>From 535b04d196d0872e5c2eeb3e1b2a2e1b3e421469 Mon Sep 17 00:00:00 2001
From: johnfav03 <64748847+johnfav03 at users.noreply.github.com>
Date: Sun, 2 Aug 2026 17:02:25 -0500
Subject: [PATCH 2/2] [NFC][KnownBits] Share rotate sign-bits logic between
SelectionDAG and GlobalISel
---
llvm/include/llvm/Support/KnownBits.h | 6 +++++
.../CodeGen/GlobalISel/GISelValueTracking.cpp | 24 +++++-------------
.../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 25 ++++++-------------
llvm/lib/Support/KnownBits.cpp | 23 +++++++++++++++++
4 files changed, 42 insertions(+), 36 deletions(-)
diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index f88e5bdd1a92d..75bd40f692f23 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -485,6 +485,12 @@ struct KnownBits {
/// Compute known bits for pdep(Val, Mask).
LLVM_ABI static KnownBits pdep(const KnownBits &Val, const KnownBits &Mask);
+ /// Compute the number of sign bits after rotating a value.
+ LLVM_ABI static unsigned rotateNumSignBits(unsigned SrcSignBits,
+ unsigned BitWidth,
+ std::optional<uint64_t> RotAmt,
+ bool IsRotateRight);
+
/// Determine if these known bits always give the same ICMP_EQ result.
LLVM_ABI static std::optional<bool> eq(const KnownBits &LHS,
const KnownBits &RHS);
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index a1188dd2468c3..dcc00ab4d42a5 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -2374,24 +2374,12 @@ unsigned GISelValueTracking::computeNumSignBits(Register R,
case TargetOpcode::G_ROTR: {
Register SrcReg = MI.getOperand(1).getReg();
unsigned Tmp = computeNumSignBits(SrcReg, DemandedElts, Depth + 1);
-
- // If we're rotating an 0/-1 value, then it stays an 0/-1 value.
- if (Tmp == TyBits)
- return TyBits;
-
- if (auto MaybeAmt =
- isConstantOrConstantSplatVector(MI.getOperand(2).getReg(), MRI)) {
- unsigned RotAmt = MaybeAmt->urem(TyBits);
-
- // Handle rotate right by N like a rotate left by TyBits-N.
- if (Opcode == TargetOpcode::G_ROTR)
- RotAmt = (TyBits - RotAmt) % TyBits;
-
- // If we aren't rotating out all of the known-in sign bits, return the
- // number that are left. This handles rotl(sext(x), 1) for example.
- if (Tmp > RotAmt + 1)
- FirstAnswer = Tmp - RotAmt;
- }
+ auto MaybeAmt =
+ isConstantOrConstantSplatVector(MI.getOperand(2).getReg(), MRI);
+ FirstAnswer = KnownBits::rotateNumSignBits(
+ Tmp, TyBits,
+ MaybeAmt ? std::optional(MaybeAmt->getZExtValue()) : std::nullopt,
+ Opcode == TargetOpcode::G_ROTR);
break;
}
case TargetOpcode::G_SREM: {
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 683cf2517b2f5..f519bca6b33d6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -5238,26 +5238,15 @@ unsigned SelectionDAG::ComputeNumSignBits(SDValue Op, const APInt &DemandedElts,
return VTBits;
break;
case ISD::ROTL:
- case ISD::ROTR:
+ case ISD::ROTR: {
Tmp = ComputeNumSignBits(Op.getOperand(0), DemandedElts, Depth + 1);
-
- // If we're rotating an 0/-1 value, then it stays an 0/-1 value.
- if (Tmp == VTBits)
- return VTBits;
-
- if (ConstantSDNode *C =
- isConstOrConstSplat(Op.getOperand(1), DemandedElts)) {
- unsigned RotAmt = C->getAPIntValue().urem(VTBits);
-
- // Handle rotate right by N like a rotate left by 32-N.
- if (Opcode == ISD::ROTR)
- RotAmt = (VTBits - RotAmt) % VTBits;
-
- // If we aren't rotating out all of the known-in sign bits, return the
- // number that are left. This handles rotl(sext(x), 1) for example.
- if (Tmp > (RotAmt + 1)) return (Tmp - RotAmt);
- }
+ ConstantSDNode *C = isConstOrConstSplat(Op.getOperand(1), DemandedElts);
+ FirstAnswer = KnownBits::rotateNumSignBits(
+ Tmp, VTBits,
+ C ? std::optional(C->getAPIntValue().getZExtValue()) : std::nullopt,
+ Opcode == ISD::ROTR);
break;
+ }
case ISD::ADD:
case ISD::ADDC:
// TODO: Move Operand 1 check before Operand 0 check
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index c3acd2936e2ee..f694437eb6d69 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -713,6 +713,29 @@ KnownBits KnownBits::pdep(const KnownBits &Val, const KnownBits &Mask) {
return Res;
}
+unsigned KnownBits::rotateNumSignBits(unsigned SrcSignBits, unsigned BitWidth,
+ std::optional<uint64_t> RotAmt,
+ bool IsRotateRight) {
+ // If we're rotating an 0/-1 value, then it stays an 0/-1 value.
+ if (SrcSignBits == BitWidth)
+ return BitWidth;
+
+ if (!RotAmt)
+ return 1;
+
+ unsigned Amt = *RotAmt % BitWidth;
+
+ // Handle rotate right by N like a rotate left by BitWidth-N.
+ if (IsRotateRight)
+ Amt = (BitWidth - Amt) % BitWidth;
+
+ // If we aren't rotating out all of the known-in sign bits, return the
+ // number that are left. This handles rotl(sext(x), 1) for example.
+ if (SrcSignBits > Amt + 1)
+ return SrcSignBits - Amt;
+ return 1;
+}
+
std::optional<bool> KnownBits::eq(const KnownBits &LHS, const KnownBits &RHS) {
if (LHS.isConstant() && RHS.isConstant())
return LHS.getConstant() == RHS.getConstant();
More information about the llvm-commits
mailing list