[llvm] 9e94ec5 - [ValueTracking] Set KnownBits from MSB shift idiom (#200203)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 09:28:41 PDT 2026


Author: Marina Taylor
Date: 2026-06-08T17:28:36+01:00
New Revision: 9e94ec5dba70d5387561647a1b3b5d83bb0d3522

URL: https://github.com/llvm/llvm-project/commit/9e94ec5dba70d5387561647a1b3b5d83bb0d3522
DIFF: https://github.com/llvm/llvm-project/commit/9e94ec5dba70d5387561647a1b3b5d83bb0d3522.diff

LOG: [ValueTracking] Set KnownBits from MSB shift idiom (#200203)

`shl X, sub(Y, xor(ctlz(X, true), BitWidth-1))` shifts X so that its MSB
lands at bit Y, when BitWidth is a power of 2.

https://alive2.llvm.org/ce/z/qknVdk

Assisted-by: claude

Added: 
    llvm/test/Analysis/ValueTracking/knownbits-shl-ctlz-msb.ll

Modified: 
    llvm/lib/Analysis/ValueTracking.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1261664c5b986..0dc9f91964e5a 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1631,6 +1631,20 @@ static void computeKnownBitsFromOperator(const Operator *I,
     const APInt *C;
     if (match(I->getOperand(0), m_APInt(C)))
       Known.Zero.setLowBits(C->countr_zero());
+
+    // shl X, sub(Y, xor(ctlz(X, true), BitWidth-1)) shifts X so that its MSB
+    // lands at bit Y, when BitWidth is a power of 2.
+    const APInt *YC;
+    Value *X = I->getOperand(0);
+    if (isPowerOf2_32(BitWidth) &&
+        match(I->getOperand(1),
+              m_Sub(m_APInt(YC), m_Xor(m_Ctlz(m_Specific(X), m_One()),
+                                       m_SpecificInt(BitWidth - 1)))) &&
+        YC->ult(BitWidth - 1)) {
+      unsigned Y = YC->getZExtValue();
+      Known.One.setBit(Y);
+      Known.Zero.setBitsFrom(Y + 1);
+    }
     break;
   }
   case Instruction::LShr: {

diff  --git a/llvm/test/Analysis/ValueTracking/knownbits-shl-ctlz-msb.ll b/llvm/test/Analysis/ValueTracking/knownbits-shl-ctlz-msb.ll
new file mode 100644
index 0000000000000..ba71154cc05f8
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/knownbits-shl-ctlz-msb.ll
@@ -0,0 +1,113 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=instcombine -S < %s | FileCheck %s
+
+; Pattern: shl X, sub(Y, xor(ctlz(X), BitWidth-1))
+; When BitWidth is a power of 2 and Y < BitWidth-1, the result has its MSB
+; at bit Y: bit Y is set and bits above Y are clear.
+
+define i32 @msb_at_14_bit_set(i32 %x) {
+; CHECK-LABEL: @msb_at_14_bit_set(
+; CHECK-NEXT:    ret i32 16384
+;
+  %c = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
+  %xored = xor i32 %c, 31
+  %shamt = sub i32 14, %xored
+  %s = shl i32 %x, %shamt
+  %r = and i32 %s, 16384
+  ret i32 %r
+}
+
+define i32 @msb_at_14_high_bits_clear(i32 %x) {
+; CHECK-LABEL: @msb_at_14_high_bits_clear(
+; CHECK-NEXT:    ret i32 0
+;
+  %c = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
+  %xored = xor i32 %c, 31
+  %shamt = sub i32 14, %xored
+  %s = shl i32 %x, %shamt
+  %r = and i32 %s, 4294934528
+  ret i32 %r
+}
+
+define <2 x i32> @msb_at_14_vec(<2 x i32> %x) {
+; CHECK-LABEL: @msb_at_14_vec(
+; CHECK-NEXT:    ret <2 x i32> splat (i32 16384)
+;
+  %c = call <2 x i32> @llvm.ctlz.v2i32(<2 x i32> %x, i1 true)
+  %xored = xor <2 x i32> %c, <i32 31, i32 31>
+  %shamt = sub <2 x i32> <i32 14, i32 14>, %xored
+  %s = shl <2 x i32> %x, %shamt
+  %r = and <2 x i32> %s, <i32 16384, i32 16384>
+  ret <2 x i32> %r
+}
+
+; Negative: Y is not less than BitWidth - 1
+define i32 @neg_y_too_large(i32 %x) {
+; CHECK-LABEL: @neg_y_too_large(
+; CHECK-NEXT:    [[C:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[X:%.*]], i1 true)
+; CHECK-NEXT:    [[S:%.*]] = shl i32 [[X]], [[C]]
+; CHECK-NEXT:    [[R:%.*]] = and i32 [[S]], -2147483648
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %c = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
+  %xored = xor i32 %c, 31
+  %shamt = sub i32 31, %xored
+  %s = shl i32 %x, %shamt
+  %r = and i32 %s, 2147483648
+  ret i32 %r
+}
+
+; Negative: BitWidth not a power of two
+define i31 @neg_bad_width(i31 %x) {
+; CHECK-LABEL: @neg_bad_width(
+; CHECK-NEXT:    [[C:%.*]] = call range(i31 0, 32) i31 @llvm.ctlz.i31(i31 [[X:%.*]], i1 true)
+; CHECK-NEXT:    [[XORED:%.*]] = xor i31 [[C]], 30
+; CHECK-NEXT:    [[SHAMT:%.*]] = sub nsw i31 14, [[XORED]]
+; CHECK-NEXT:    [[S:%.*]] = shl i31 [[X]], [[SHAMT]]
+; CHECK-NEXT:    [[R:%.*]] = and i31 [[S]], 16384
+; CHECK-NEXT:    ret i31 [[R]]
+;
+  %c = call i31 @llvm.ctlz.i31(i31 %x, i1 true)
+  %xored = xor i31 %c, 30
+  %shamt = sub i31 14, %xored
+  %s = shl i31 %x, %shamt
+  %r = and i31 %s, 16384
+  ret i31 %r
+}
+
+; Negative: xor with a constant other than BitWidth-1 breaks the equivalence
+; ctlz(X) ^ K == (BitWidth-1) - ctlz(X), so the MSB position claim doesn't hold.
+define i32 @neg_wrong_xor_const(i32 %x) {
+; CHECK-LABEL: @neg_wrong_xor_const(
+; CHECK-NEXT:    [[C:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[X:%.*]], i1 true)
+; CHECK-NEXT:    [[XORED:%.*]] = xor i32 [[C]], 30
+; CHECK-NEXT:    [[SHAMT:%.*]] = sub nsw i32 14, [[XORED]]
+; CHECK-NEXT:    [[S:%.*]] = shl i32 [[X]], [[SHAMT]]
+; CHECK-NEXT:    [[R:%.*]] = and i32 [[S]], 16384
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %c = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
+  %xored = xor i32 %c, 30
+  %shamt = sub i32 14, %xored
+  %s = shl i32 %x, %shamt
+  %r = and i32 %s, 16384
+  ret i32 %r
+}
+
+; Negative: the value being shifted is not the value fed to ctlz.
+define i32 @neg_
diff erent_x(i32 %x, i32 %y) {
+; CHECK-LABEL: @neg_
diff erent_x(
+; CHECK-NEXT:    [[C:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[Y:%.*]], i1 true)
+; CHECK-NEXT:    [[XORED:%.*]] = xor i32 [[C]], 31
+; CHECK-NEXT:    [[SHAMT:%.*]] = sub nsw i32 14, [[XORED]]
+; CHECK-NEXT:    [[S:%.*]] = shl i32 [[X:%.*]], [[SHAMT]]
+; CHECK-NEXT:    [[R:%.*]] = and i32 [[S]], 16384
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %c = call i32 @llvm.ctlz.i32(i32 %y, i1 true)
+  %xored = xor i32 %c, 31
+  %shamt = sub i32 14, %xored
+  %s = shl i32 %x, %shamt
+  %r = and i32 %s, 16384
+  ret i32 %r
+}


        


More information about the llvm-commits mailing list