[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