From b802455c60365faf15d2225726d8da788c982145 Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Tue, 15 Jul 2025 19:06:13 +0300 Subject: [PATCH 01/11] [LLVM] Enhance shift operators in the Demanded Bits Analysis This is done by supporting shift operators to handle non constant shift amount. --- llvm/lib/Analysis/DemandedBits.cpp | 46 ++++++++++++++++++++++++ llvm/test/Analysis/DemandedBits/shl.ll | 48 +++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 6694d5cc06c8c..2d30575c19130 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/KnownBits.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -183,6 +184,17 @@ void DemandedBits::determineLiveOperandBits( AB |= APInt::getHighBitsSet(BitWidth, ShiftAmt+1); else if (S->hasNoUnsignedWrap()) AB |= APInt::getHighBitsSet(BitWidth, ShiftAmt); + } else { + ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); + unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); + unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); + // similar to Lshr case + AB = (AOut.lshr(Min) | AOut.lshr(Max)); + const auto *S = cast(UserI); + if (S->hasNoSignedWrap()) + AB |= APInt::getHighBitsSet(BitWidth, Max + 1); + else if (S->hasNoUnsignedWrap()) + AB |= APInt::getHighBitsSet(BitWidth, Max); } } break; @@ -197,6 +209,19 @@ void DemandedBits::determineLiveOperandBits( // (they must be zero). if (cast(UserI)->isExact()) AB |= APInt::getLowBitsSet(BitWidth, ShiftAmt); + } else { + ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); + unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); + unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); + // Suppose AOut == 0b0000 0011 + // [min, max] = [1, 3] + // shift by 1 we get 0b0000 0110 + // shift by 2 we get 0b0000 1100 + // shift by 3 we get 0b0001 1000 + // we take the or here because need to cover all the above possibilities + AB = (AOut.shl(Min) | AOut.shl(Max)); + if (cast(UserI)->isExact()) + AB |= APInt::getLowBitsSet(BitWidth, Max); } } break; @@ -217,6 +242,27 @@ void DemandedBits::determineLiveOperandBits( // (they must be zero). if (cast(UserI)->isExact()) AB |= APInt::getLowBitsSet(BitWidth, ShiftAmt); + } else { + ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); + unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); + unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); + AB = (AOut.shl(Min) | AOut.shl(Max)); + + if (Max) { + // Suppose AOut = 0011 1100 + // [min, max] = [1, 3] + // ShiftAmount = 1 : Mask is 1000 0000 + // ShiftAmount = 2 : Mask is 1100 0000 + // ShiftAmount = 3 : Mask is 1110 0000 + // The Mask with Max covers every case in [min, max], + // so we are done + if ((AOut & APInt::getHighBitsSet(BitWidth, Max)).getBoolValue()) + AB.setSignBit(); + } + // If the shift is exact, then the low bits are not dead + // (they must be zero). + if (cast(UserI)->isExact()) + AB |= APInt::getLowBitsSet(BitWidth, Max); } } break; diff --git a/llvm/test/Analysis/DemandedBits/shl.ll b/llvm/test/Analysis/DemandedBits/shl.ll index e41f5f4107735..c3313a93c1e85 100644 --- a/llvm/test/Analysis/DemandedBits/shl.ll +++ b/llvm/test/Analysis/DemandedBits/shl.ll @@ -57,10 +57,56 @@ define i8 @test_shl(i32 %a, i32 %b) { ; CHECK-DAG: DemandedBits: 0xff for %shl.t = trunc i32 %shl to i8 ; CHECK-DAG: DemandedBits: 0xff for %shl in %shl.t = trunc i32 %shl to i8 ; CHECK-DAG: DemandedBits: 0xff for %shl = shl i32 %a, %b -; CHECK-DAG: DemandedBits: 0xffffffff for %a in %shl = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xff for %a in %shl = shl i32 %a, %b ; CHECK-DAG: DemandedBits: 0xffffffff for %b in %shl = shl i32 %a, %b ; %shl = shl i32 %a, %b %shl.t = trunc i32 %shl to i8 ret i8 %shl.t } + +define i8 @test_shl_var_amount(i32 %a, i32 %b){ +; CHECK-LABEL: 'test_shl_var_amount' +; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 +; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 +; CHECK-DAG: DemandedBits: 0xff for %4 = shl i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xff for %1 in %4 = shl i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = shl i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8 +; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 +; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 +; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 +; CHECK-DAG: DemandedBits: 0xff for %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xff for %a in %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xff for %b in %1 = add nsw i32 %a, %b +; + %1 = add nsw i32 %a, %b + %2 = trunc i32 %1 to i8 + %3 = zext i8 %2 to i32 + %4 = shl i32 %1, %3 + %5 = trunc i32 %4 to i8 + ret i8 %5 +} + +define i8 @test_shl_var_amount_nsw(i32 %a, i32 %b){ + ; CHECK-LABEL 'test_shl_var_amount_nsw' + ; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 + ; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 + ; CHECK-DAG: DemandedBits: 0xff for %4 = shl nsw i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xffffffff for %1 in %4 = shl nsw i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = shl nsw i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 + ; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 + ; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8 + ; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 + ; CHECK-DAG: DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b + ; + %1 = add nsw i32 %a, %b + %2 = trunc i32 %1 to i8 + %3 = zext i8 %2 to i32 + %4 = shl nsw i32 %1, %3 + %5 = trunc i32 %4 to i8 + ret i8 %5 +} From 289fb1cf05a2d3cf97099e96988459cf5e5363c1 Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Tue, 15 Jul 2025 23:54:04 +0300 Subject: [PATCH 02/11] [LLVM] created new tests for lshr and ashr, and updated the Range handle non continued bits for AOut --- llvm/lib/Analysis/DemandedBits.cpp | 35 +++++-- llvm/test/Analysis/DemandedBits/ashr.ll | 112 +++++++++++++++++++++ llvm/test/Analysis/DemandedBits/lshr.ll | 123 ++++++++++++++++++++++++ 3 files changed, 260 insertions(+), 10 deletions(-) create mode 100644 llvm/test/Analysis/DemandedBits/ashr.ll create mode 100644 llvm/test/Analysis/DemandedBits/lshr.ll diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 2d30575c19130..9c198224d0651 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -36,7 +36,6 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/KnownBits.h" -#include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -77,6 +76,21 @@ void DemandedBits::determineLiveOperandBits( computeKnownBits(V2, Known2, DL, &AC, UserI, &DT); } }; + auto GetShiftedRange = [&](unsigned const Min, unsigned const Max, + bool ShiftLeft) { + APInt Range = APInt::getLowBitsSet(BitWidth, Max + 1) & + ~APInt::getLowBitsSet(BitWidth, Min); + using ShiftFn = APInt (APInt::*)(unsigned) const; // one-word shift overload + auto Shift = ShiftLeft ? static_cast(&APInt::shl) + : static_cast(&APInt::lshr); + AB = APInt::getZero(BitWidth); + APInt Bits = AOut; + while (Bits != 0) { + unsigned I = Bits.countr_zero(); + AB |= (Range.*Shift)(I); + Bits.clearBit(I); + } + }; switch (UserI->getOpcode()) { default: break; @@ -189,7 +203,7 @@ void DemandedBits::determineLiveOperandBits( unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); // similar to Lshr case - AB = (AOut.lshr(Min) | AOut.lshr(Max)); + GetShiftedRange(Min, Max, false); const auto *S = cast(UserI); if (S->hasNoSignedWrap()) AB |= APInt::getHighBitsSet(BitWidth, Max + 1); @@ -213,13 +227,15 @@ void DemandedBits::determineLiveOperandBits( ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); - // Suppose AOut == 0b0000 0011 + // Suppose AOut == 0b0000 1001 // [min, max] = [1, 3] - // shift by 1 we get 0b0000 0110 - // shift by 2 we get 0b0000 1100 - // shift by 3 we get 0b0001 1000 - // we take the or here because need to cover all the above possibilities - AB = (AOut.shl(Min) | AOut.shl(Max)); + // we create the range 0b0000 1110 + // shift by 1 we get 0b0001 1100 + // shift by 2 we get 0b0011 1000 + // shift by 3 we get 0b0111 0000 + // we take the or for every shift to cover all the positions. + // + GetShiftedRange(Min, Max, true); if (cast(UserI)->isExact()) AB |= APInt::getLowBitsSet(BitWidth, Max); } @@ -246,8 +262,7 @@ void DemandedBits::determineLiveOperandBits( ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); - AB = (AOut.shl(Min) | AOut.shl(Max)); - + GetShiftedRange(Min, Max, true); if (Max) { // Suppose AOut = 0011 1100 // [min, max] = [1, 3] diff --git a/llvm/test/Analysis/DemandedBits/ashr.ll b/llvm/test/Analysis/DemandedBits/ashr.ll new file mode 100644 index 0000000000000..79e9a8c086256 --- /dev/null +++ b/llvm/test/Analysis/DemandedBits/ashr.ll @@ -0,0 +1,112 @@ +; RUN: opt -S -disable-output -passes="print" < %s 2>&1 | FileCheck %s + +define i8 @test_ashr_const_amount_4(i32 %a) { +; CHECK-LABEL: 'test_ashr_const_amount_4' +; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 4 +; CHECK-DAG: DemandedBits: 0xff0 for %a in %ashr = ashr i32 %a, 4 +; CHECK-DAG: DemandedBits: 0xffffffff for 4 in %ashr = ashr i32 %a, 4 +; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8 +; + %ashr = ashr i32 %a, 4 + %ashr.t = trunc i32 %ashr to i8 + ret i8 %ashr.t +} + +define i8 @test_ashr_const_amount_5(i32 %a) { +; CHECK-LABEL: 'test_ashr_const_amount_5' +; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 5 +; CHECK-DAG: DemandedBits: 0x1fe0 for %a in %ashr = ashr i32 %a, 5 +; CHECK-DAG: DemandedBits: 0xffffffff for 5 in %ashr = ashr i32 %a, 5 +; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8 +; + %ashr = ashr i32 %a, 5 + %ashr.t = trunc i32 %ashr to i8 + ret i8 %ashr.t +} + +define i8 @test_ashr_const_amount_8(i32 %a) { +; CHECK-LABEL: 'test_ashr_const_amount_8' +; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 8 +; CHECK-DAG: DemandedBits: 0xff00 for %a in %ashr = ashr i32 %a, 8 +; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %ashr = ashr i32 %a, 8 +; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8 +; + %ashr = ashr i32 %a, 8 + %ashr.t = trunc i32 %ashr to i8 + ret i8 %ashr.t +} + +define i8 @test_ashr_const_amount_9(i32 %a) { + +; CHECK-LABEL: 'test_ashr_const_amount_9' +; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 8 +; CHECK-DAG: DemandedBits: 0xff00 for %a in %ashr = ashr i32 %a, 8 +; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %ashr = ashr i32 %a, 8 +; + %ashr = ashr i32 %a, 8 + %ashr.t = trunc i32 %ashr to i8 + ret i8 %ashr.t +} + +define i8 @test_ashr(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_ashr' +; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8 +; + %ashr = ashr i32 %a, %b + %ashr.t = trunc i32 %ashr to i8 + ret i8 %ashr.t +} + +define i8 @test_ashr_var_amount(i32 %a, i32 %b){ +; CHECK-LABEL: 'test_ashr_var_amount' +; CHECK-DAG: DemandedBits: 0xff for %4 = ashr i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xffffffff for %1 in %4 = ashr i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = ashr i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8 +; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 +; CHECK-DAG: DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 +; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 +; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 +; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 +; + %1 = add nsw i32 %a, %b + %2 = trunc i32 %1 to i8 + %3 = zext i8 %2 to i32 + %4 = ashr i32 %1, %3 + %5 = trunc i32 %4 to i8 + ret i8 %5 +} + + ; CHECK-LABEL 'test_ashr_var_amount_nsw' + ; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 + ; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 + ; CHECK-DAG: DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8 + ; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 + ; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 + ; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 + ; CHECK-DAG: DemandedBits: 0xff for %4 = ashr exact i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xffffffff for %1 in %4 = ashr exact i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = ashr exact i32 %1, %3 + ; + %1 = add nsw i32 %a, %b + %2 = trunc i32 %1 to i8 + %3 = zext i8 %2 to i32 + %4 = ashr exact i32 %1, %3 + %5 = trunc i32 %4 to i8 + ret i8 %5 +} diff --git a/llvm/test/Analysis/DemandedBits/lshr.ll b/llvm/test/Analysis/DemandedBits/lshr.ll new file mode 100644 index 0000000000000..81c7da4ca8b38 --- /dev/null +++ b/llvm/test/Analysis/DemandedBits/lshr.ll @@ -0,0 +1,123 @@ +; RUN: opt -S -disable-output -passes="print" < %s 2>&1 | FileCheck %s + +define i8 @test_lshr_const_amount_4(i32 %a) { +; CHECK-LABEL: 'test_lshr_const_amount_4' +; CHECK-DAG: DemandedBits: 0xff for %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr in %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr = lshr i32 %a, 4 +; CHECK-DAG: DemandedBits: 0xff0 for %a in %lshr = lshr i32 %a, 4 +; CHECK-DAG: DemandedBits: 0xffffffff for 4 in %lshr = lshr i32 %a, 4 +; + %lshr = lshr i32 %a, 4 + %lshr.t = trunc i32 %lshr to i8 + ret i8 %lshr.t +} + +define i8 @test_lshr_const_amount_5(i32 %a) { +; CHECK-LABEL: 'test_lshr_const_amount_5' +; CHECK-DAG: DemandedBits: 0xff for %lshr = lshr i32 %a, 5 +; CHECK-DAG: DemandedBits: 0x1fe0 for %a in %lshr = lshr i32 %a, 5 +; CHECK-DAG: DemandedBits: 0xffffffff for 5 in %lshr = lshr i32 %a, 5 +; CHECK-DAG: DemandedBits: 0xff for %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr in %lshr.t = trunc i32 %lshr to i8 +; + %lshr = lshr i32 %a, 5 + %lshr.t = trunc i32 %lshr to i8 + ret i8 %lshr.t +} +define i8 @test_lshr_const_amount_8(i32 %a) { +; CHECK-LABEL: 'test_lshr_const_amount_8' +; CHECK-DAG: DemandedBits: 0xff for %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr in %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr = lshr i32 %a, 8 +; CHECK-DAG: DemandedBits: 0xff00 for %a in %lshr = lshr i32 %a, 8 +; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %lshr = lshr i32 %a, 8 +; + %lshr = lshr i32 %a, 8 + %lshr.t = trunc i32 %lshr to i8 + ret i8 %lshr.t +} + +define i8 @test_lshr_const_amount_9(i32 %a) { +; CHECK-LABEL: 'test_lshr_const_amount_9' +; CHECK-DAG: DemandedBits: 0xff for %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr in %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr = lshr i32 %a, 9 +; CHECK-DAG: DemandedBits: 0x1fe00 for %a in %lshr = lshr i32 %a, 9 +; CHECK-DAG: DemandedBits: 0xffffffff for 9 in %lshr = lshr i32 %a, 9 +; + %lshr = lshr i32 %a, 9 + %lshr.t = trunc i32 %lshr to i8 + ret i8 %lshr.t +} + +define i8 @test_lshr(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_lshr' +; CHECK-DAG: DemandedBits: 0xff for %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xff for %lshr.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr in %lshr.t = trunc i32 %lshr to i8 +; + %lshr = lshr i32 %a, %b + %lshr.t = trunc i32 %lshr to i8 + ret i8 %lshr.t +} +DemandedBits: 0xff for %4 = lshr i32 %1, %3 +DemandedBits: 0xffffffff for %1 in %4 = lshr i32 %1, %3 +DemandedBits: 0xffffffff for %3 in %4 = lshr i32 %1, %3 +DemandedBits: 0xff for %5 = trunc i32 %4 to i8 +DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 +DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 +DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 +DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b +DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b +DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b +DemandedBits: 0xff for %2 = trunc i32 %1 to i8 +DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 + +define i8 @test_lshr_var_amount(i32 %a, i32 %b){ +; CHECK-LABEL: 'test_lshr_var_amount' +; CHECK-DAG: DemandedBits: 0xff for %4 = lshr i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xffffffff for %1 in %4 = lshr i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = lshr i32 %1, %3 +; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 +; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 +; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 +; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 +; CHECK-DAG: DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b +; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8 +; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 +; + %1 = add nsw i32 %a, %b + %2 = trunc i32 %1 to i8 + %3 = zext i8 %2 to i32 + %4 = lshr i32 %1, %3 + %5 = trunc i32 %4 to i8 + ret i8 %5 +} + +define i8 @test_lshr_var_amount_exact(i32 %a, i32 %b){ + ; CHECK-LABEL 'test_lshr_var_amount_nsw' + ; CHECK-DAG: DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b + ; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8 + ; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 + ; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 + ; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 + ; CHECK-DAG: DemandedBits: 0xff for %4 = lshr exact i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xffffffff for %1 in %4 = lshr exact i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = lshr exact i32 %1, %3 + ; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 + ; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 + ; + %1 = add nsw i32 %a, %b + %2 = trunc i32 %1 to i8 + %3 = zext i8 %2 to i32 + %4 = lshr exact i32 %1, %3 + %5 = trunc i32 %4 to i8 + ret i8 %5 +} From 3a4d65e76804d70ae254642ae6f7804040e19118 Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Wed, 16 Jul 2025 12:11:36 +0300 Subject: [PATCH 03/11] removed comment --- llvm/lib/Analysis/DemandedBits.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 9c198224d0651..35cdb5d78e225 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -80,7 +80,7 @@ void DemandedBits::determineLiveOperandBits( bool ShiftLeft) { APInt Range = APInt::getLowBitsSet(BitWidth, Max + 1) & ~APInt::getLowBitsSet(BitWidth, Min); - using ShiftFn = APInt (APInt::*)(unsigned) const; // one-word shift overload + using ShiftFn = APInt (APInt::*)(unsigned) const; auto Shift = ShiftLeft ? static_cast(&APInt::shl) : static_cast(&APInt::lshr); AB = APInt::getZero(BitWidth); From 4fbabd66efd027f4f70aec9aef742b1b504cf1dc Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Wed, 16 Jul 2025 22:55:04 +0300 Subject: [PATCH 04/11] fixed or-->trunc->shl error, by updating the GetShiftedRange, needs more checks --- llvm/lib/Analysis/DemandedBits.cpp | 22 ++++++++-------------- llvm/test/Analysis/DemandedBits/ashr.ll | 1 + llvm/test/Analysis/DemandedBits/lshr.ll | 12 ------------ 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 35cdb5d78e225..292f610f5e8c1 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -78,17 +78,12 @@ void DemandedBits::determineLiveOperandBits( }; auto GetShiftedRange = [&](unsigned const Min, unsigned const Max, bool ShiftLeft) { - APInt Range = APInt::getLowBitsSet(BitWidth, Max + 1) & - ~APInt::getLowBitsSet(BitWidth, Min); using ShiftFn = APInt (APInt::*)(unsigned) const; auto Shift = ShiftLeft ? static_cast(&APInt::shl) : static_cast(&APInt::lshr); AB = APInt::getZero(BitWidth); - APInt Bits = AOut; - while (Bits != 0) { - unsigned I = Bits.countr_zero(); - AB |= (Range.*Shift)(I); - Bits.clearBit(I); + for (unsigned ShiftAmount = Min; ShiftAmount <= Max; ++ShiftAmount) { + AB |= (AOut.*Shift)(ShiftAmount); } }; @@ -203,7 +198,7 @@ void DemandedBits::determineLiveOperandBits( unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); // similar to Lshr case - GetShiftedRange(Min, Max, false); + GetShiftedRange(Min, Max, /*ShiftLeft=*/false); const auto *S = cast(UserI); if (S->hasNoSignedWrap()) AB |= APInt::getHighBitsSet(BitWidth, Max + 1); @@ -229,13 +224,12 @@ void DemandedBits::determineLiveOperandBits( unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); // Suppose AOut == 0b0000 1001 // [min, max] = [1, 3] - // we create the range 0b0000 1110 - // shift by 1 we get 0b0001 1100 - // shift by 2 we get 0b0011 1000 - // shift by 3 we get 0b0111 0000 + // shift by 1 we get 0b0001 00100 + // shift by 2 we get 0b0010 0100 + // shift by 3 we get 0b0100 1000 // we take the or for every shift to cover all the positions. // - GetShiftedRange(Min, Max, true); + GetShiftedRange(Min, Max, /*ShiftLeft=*/true); if (cast(UserI)->isExact()) AB |= APInt::getLowBitsSet(BitWidth, Max); } @@ -262,7 +256,7 @@ void DemandedBits::determineLiveOperandBits( ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); - GetShiftedRange(Min, Max, true); + GetShiftedRange(Min, Max, /*ShiftLeft=*/true); if (Max) { // Suppose AOut = 0011 1100 // [min, max] = [1, 3] diff --git a/llvm/test/Analysis/DemandedBits/ashr.ll b/llvm/test/Analysis/DemandedBits/ashr.ll index 79e9a8c086256..7bbb1f1bd6c54 100644 --- a/llvm/test/Analysis/DemandedBits/ashr.ll +++ b/llvm/test/Analysis/DemandedBits/ashr.ll @@ -89,6 +89,7 @@ define i8 @test_ashr_var_amount(i32 %a, i32 %b){ ret i8 %5 } +define i8 @test_ashr_var_amount_nsw(i32 %a, i32 %b){ ; CHECK-LABEL 'test_ashr_var_amount_nsw' ; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 ; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 diff --git a/llvm/test/Analysis/DemandedBits/lshr.ll b/llvm/test/Analysis/DemandedBits/lshr.ll index 81c7da4ca8b38..3d47cacf73787 100644 --- a/llvm/test/Analysis/DemandedBits/lshr.ll +++ b/llvm/test/Analysis/DemandedBits/lshr.ll @@ -63,18 +63,6 @@ define i8 @test_lshr(i32 %a, i32 %b) { %lshr.t = trunc i32 %lshr to i8 ret i8 %lshr.t } -DemandedBits: 0xff for %4 = lshr i32 %1, %3 -DemandedBits: 0xffffffff for %1 in %4 = lshr i32 %1, %3 -DemandedBits: 0xffffffff for %3 in %4 = lshr i32 %1, %3 -DemandedBits: 0xff for %5 = trunc i32 %4 to i8 -DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8 -DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32 -DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32 -DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b -DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b -DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b -DemandedBits: 0xff for %2 = trunc i32 %1 to i8 -DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8 define i8 @test_lshr_var_amount(i32 %a, i32 %b){ ; CHECK-LABEL: 'test_lshr_var_amount' From 23cea682e5a3d9f5f495b3af9f4feb68472f40c3 Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Thu, 17 Jul 2025 17:55:36 +0300 Subject: [PATCH 05/11] added more range tests --- llvm/test/Analysis/DemandedBits/ashr.ll | 85 ++++++++++++++++++++++++ llvm/test/Analysis/DemandedBits/lshr.ll | 87 +++++++++++++++++++++++++ llvm/test/Analysis/DemandedBits/shl.ll | 86 ++++++++++++++++++++++++ 3 files changed, 258 insertions(+) diff --git a/llvm/test/Analysis/DemandedBits/ashr.ll b/llvm/test/Analysis/DemandedBits/ashr.ll index 7bbb1f1bd6c54..6185d4c492d86 100644 --- a/llvm/test/Analysis/DemandedBits/ashr.ll +++ b/llvm/test/Analysis/DemandedBits/ashr.ll @@ -66,6 +66,91 @@ define i8 @test_ashr(i32 %a, i32 %b) { ret i8 %ashr.t } +define i8 @test_ashr_range_1(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_ashr_range_1' +; CHECK-DAG: DemandedBits: 0xff for %shl.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xff for %ashr in %shl.t = trunc i32 %ashr to i8 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0x7ff for %a in %ashr = ashr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %ashr = ashr i32 %a, %b2 +; + %b2 = and i32 %b, 3 + %ashr = ashr i32 %a, %b2 + %shl.t = trunc i32 %ashr to i8 + ret i8 %shl.t +} + +define i32 @test_ashr_range_2(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_ashr_range_2' +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for %ashr = ashr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %ashr = ashr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %ashr = ashr i32 %a, %b2 +; + %b2 = and i32 %b, 3 + %ashr = ashr i32 %a, %b2 + ret i32 %ashr +} + +define i32 @test_ashr_range_3(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_ashr_range_3' +; CHECK-DAG: DemandedBits: 0xffff for %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %shl = shl i32 %ashr, 16 +; CHECK-DAG: DemandedBits: 0xffff for %ashr in %shl = shl i32 %ashr, 16 +; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %shl = shl i32 %ashr, 16 +; + %ashr = ashr i32 %a, %b + %shl = shl i32 %ashr, 16 + ret i32 %shl +} +define i32 @test_ashr_range_4(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_ashr_range_4' +; CHECK-DAG: DemandedBits: 0xffffffff for %shr = lshr i32 %ashr, 8 +; CHECK-DAG: DemandedBits: 0xffffff00 for %ashr in %shr = lshr i32 %ashr, 8 +; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %shr = lshr i32 %ashr, 8 +; CHECK-DAG: DemandedBits: 0xffffff00 for %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffff00 for %a in %ashr = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %ashr = ashr i32 %a, %b + %ashr = ashr i32 %a, %b + %shr = lshr i32 %ashr, 8 + ret i32 %shr +} + +define i32 @test_ashr_range_5(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_ashr_range_5' +; CHECK-DAG: DemandedBits: 0xffffffff for %2 = and i32 %1, 255 +; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = and i32 %1, 255 +; CHECK-DAG: DemandedBits: 0xffffffff for 255 in %2 = and i32 %1, 255 +; CHECK-DAG: DemandedBits: 0xff for %1 = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = ashr i32 %a, %b +; + %1 = ashr i32 %a, %b + %2 = and i32 %1, 255 + ret i32 %2 +} + +define i32 @test_ashr_range_6(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_ashr_range_6' +; CHECK-DAG: DemandedBits: 0xffff0000 for %lshr.1 = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffff0000 for %a in %lshr.1 = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %lshr.1 = ashr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %lshr.2 = ashr i32 %lshr.1, 16 +; CHECK-DAG: DemandedBits: 0xffff0000 for %lshr.1 in %lshr.2 = ashr i32 %lshr.1, 16 +; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %lshr.2 = ashr i32 %lshr.1, 16 +; + %lshr.1 = ashr i32 %a, %b + %lshr.2 = ashr i32 %lshr.1, 16 + ret i32 %lshr.2 +} + define i8 @test_ashr_var_amount(i32 %a, i32 %b){ ; CHECK-LABEL: 'test_ashr_var_amount' ; CHECK-DAG: DemandedBits: 0xff for %4 = ashr i32 %1, %3 diff --git a/llvm/test/Analysis/DemandedBits/lshr.ll b/llvm/test/Analysis/DemandedBits/lshr.ll index 3d47cacf73787..e07f994a1b304 100644 --- a/llvm/test/Analysis/DemandedBits/lshr.ll +++ b/llvm/test/Analysis/DemandedBits/lshr.ll @@ -64,6 +64,93 @@ define i8 @test_lshr(i32 %a, i32 %b) { ret i8 %lshr.t } +define i8 @test_lshr_range_1(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_lshr_range_1' +; CHECK-DAG: DemandedBits: 0xff for %shl.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr in %shl.t = trunc i32 %lshr to i8 +; CHECK-DAG: DemandedBits: 0xff for %lshr = lshr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0x7ff for %a in %lshr = lshr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %lshr = lshr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3 +; + %b2 = and i32 %b, 3 + %lshr = lshr i32 %a, %b2 + %shl.t = trunc i32 %lshr to i8 + ret i8 %shl.t +} + +define i32 @test_lshr_range_2(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_lshr_range_2' +; CHECK-DAG: DemandedBits: 0xffffffff for %lshr = lshr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %lshr = lshr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %lshr = lshr i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3 +; + %b2 = and i32 %b, 3 + %lshr = lshr i32 %a, %b2 + ret i32 %lshr +} + +define i32 @test_lshr_range_3(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_lshr_range_3' +; CHECK-DAG: DemandedBits: 0xffff for %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %shl = shl i32 %lshr, 16 +; CHECK-DAG: DemandedBits: 0xffff for %lshr in %shl = shl i32 %lshr, 16 +; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %shl = shl i32 %lshr, 16 +; + %lshr = lshr i32 %a, %b + %shl = shl i32 %lshr, 16 + ret i32 %shl +} + +define i32 @test_lshr_range_4(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_lshr_range_4' +; CHECK-DAG: DemandedBits: 0xffffff00 for %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffff00 for %a in %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %lshr = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %shr = ashr i32 %lshr, 8 +; CHECK-DAG: DemandedBits: 0xffffff00 for %lshr in %shr = ashr i32 %lshr, 8 +; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %shr = ashr i32 %lshr, 8 + %lshr = lshr i32 %a, %b + %shr = ashr i32 %lshr, 8 + ret i32 %shr +} + +define i32 @test_lshr_range_5(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_lshr_range_5' +; CHECK-DAG: DemandedBits: 0xff for %1 = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %2 = and i32 %1, 255 +; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = and i32 %1, 255 +; CHECK-DAG: DemandedBits: 0xffffffff for 255 in %2 = and i32 %1, 255 +; + %1 = lshr i32 %a, %b + %2 = and i32 %1, 255 + ret i32 %2 +} + +define i32 @test_lshr_range_6(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_lshr_range_6' +; CHECK-DAG: DemandedBits: 0xffff0000 for %lshr.1 = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffff0000 for %a in %lshr.1 = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %lshr.1 = lshr i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %lshr.2 = lshr i32 %lshr.1, 16 +; CHECK-DAG: DemandedBits: 0xffff0000 for %lshr.1 in %lshr.2 = lshr i32 %lshr.1, 16 +; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %lshr.2 = lshr i32 %lshr.1, 16 +; + %lshr.1 = lshr i32 %a, %b + %lshr.2 = lshr i32 %lshr.1, 16 + ret i32 %lshr.2 +} + + define i8 @test_lshr_var_amount(i32 %a, i32 %b){ ; CHECK-LABEL: 'test_lshr_var_amount' ; CHECK-DAG: DemandedBits: 0xff for %4 = lshr i32 %1, %3 diff --git a/llvm/test/Analysis/DemandedBits/shl.ll b/llvm/test/Analysis/DemandedBits/shl.ll index c3313a93c1e85..c872d2d854e83 100644 --- a/llvm/test/Analysis/DemandedBits/shl.ll +++ b/llvm/test/Analysis/DemandedBits/shl.ll @@ -65,6 +65,92 @@ define i8 @test_shl(i32 %a, i32 %b) { ret i8 %shl.t } +define i8 @test_shl_range_1(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_shl_range_1' +; CHECK-DAG: DemandedBits: 0xff for %shl = shl i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xff for %a in %shl = shl i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %shl = shl i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xff for %shl.t = trunc i32 %shl to i8 +; CHECK-DAG: DemandedBits: 0xff for %shl in %shl.t = trunc i32 %shl to i8 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3 +; + %b2 = and i32 %b, 3 + %shl = shl i32 %a, %b2 + %shl.t = trunc i32 %shl to i8 + ret i8 %shl.t +} + +define i32 @test_shl_range_2(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_shl_range_2' +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3 +; CHECK-DAG: DemandedBits: 0xffffffff for %shl = shl i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %shl = shl i32 %a, %b2 +; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %shl = shl i32 %a, %b2 +; + %b2 = and i32 %b, 3 + %shl = shl i32 %a, %b2 + ret i32 %shl +} + +define i32 @test_shl_range_3(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_shl_range_3' +; CHECK-DAG: DemandedBits: 0xffffffff for %shr = lshr i32 %shl, 16 +; CHECK-DAG: DemandedBits: 0xffff0000 for %shl in %shr = lshr i32 %shl, 16 +; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %shr = lshr i32 %shl, 16 +; CHECK-DAG: DemandedBits: 0xffff0000 for %shl = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %shl = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %shl = shl i32 %a, %b +; + %shl = shl i32 %a, %b + %shr = lshr i32 %shl, 16 + ret i32 %shr +} + +define i32 @test_shl_range_4(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_shl_range_4' +; CHECK-DAG: DemandedBits: 0xffffffff for %shr = ashr i32 %shl, 8 +; CHECK-DAG: DemandedBits: 0xffffff00 for %shl in %shr = ashr i32 %shl, 8 +; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %shr = ashr i32 %shl, 8 +; CHECK-DAG: DemandedBits: 0xffffff00 for %shl = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %a in %shl = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %shl = shl i32 %a, %b + %shl = shl i32 %a, %b + %shr = ashr i32 %shl, 8 + ret i32 %shr +} + +define i32 @test_shl_range_5(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_shl_range_5' +; CHECK-DAG: DemandedBits: 0xff for %1 = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xff for %a in %1 = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %2 = and i32 %1, 255 +; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = and i32 %1, 255 +; CHECK-DAG: DemandedBits: 0xffffffff for 255 in %2 = and i32 %1, 255 +; + %1 = shl i32 %a, %b + %2 = and i32 %1, 255 + ret i32 %2 +} + +define i32 @test_shl_range_6(i32 %a, i32 %b) { +; CHECK-LABEL: 'test_shl_range_6' +; CHECK-DAG: DemandedBits: 0xffffffff for %shl.2 = shl i32 %shl.1, 16 +; CHECK-DAG: DemandedBits: 0xffff for %shl.1 in %shl.2 = shl i32 %shl.1, 16 +; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %shl.2 = shl i32 %shl.1, 16 +; CHECK-DAG: DemandedBits: 0xffff for %shl.1 = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffff for %a in %shl.1 = shl i32 %a, %b +; CHECK-DAG: DemandedBits: 0xffffffff for %b in %shl.1 = shl i32 %a, %b +; + %shl.1 = shl i32 %a, %b + %shl.2 = shl i32 %shl.1, 16 + ret i32 %shl.2 +} + define i8 @test_shl_var_amount(i32 %a, i32 %b){ ; CHECK-LABEL: 'test_shl_var_amount' ; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8 From 0ee0e032c4ff338b24eda658de96c849832a2f1e Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Mon, 21 Jul 2025 14:52:34 +0300 Subject: [PATCH 06/11] removed wrong const and exceeding bit --- llvm/lib/Analysis/DemandedBits.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 292f610f5e8c1..2cafd32098032 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -76,8 +76,7 @@ void DemandedBits::determineLiveOperandBits( computeKnownBits(V2, Known2, DL, &AC, UserI, &DT); } }; - auto GetShiftedRange = [&](unsigned const Min, unsigned const Max, - bool ShiftLeft) { + auto GetShiftedRange = [&](unsigned Min, unsigned Max, bool ShiftLeft) { using ShiftFn = APInt (APInt::*)(unsigned) const; auto Shift = ShiftLeft ? static_cast(&APInt::shl) : static_cast(&APInt::lshr); @@ -224,7 +223,7 @@ void DemandedBits::determineLiveOperandBits( unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); // Suppose AOut == 0b0000 1001 // [min, max] = [1, 3] - // shift by 1 we get 0b0001 00100 + // shift by 1 we get 0b0001 0010 // shift by 2 we get 0b0010 0100 // shift by 3 we get 0b0100 1000 // we take the or for every shift to cover all the positions. From bbb1bd8247ac487d661227247b475f46e0beb91d Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Mon, 28 Jul 2025 15:08:18 +0300 Subject: [PATCH 07/11] optimized loop to nlog(n) time complexity! --- llvm/lib/Analysis/DemandedBits.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 2cafd32098032..695ae162d134b 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -81,9 +81,13 @@ void DemandedBits::determineLiveOperandBits( auto Shift = ShiftLeft ? static_cast(&APInt::shl) : static_cast(&APInt::lshr); AB = APInt::getZero(BitWidth); - for (unsigned ShiftAmount = Min; ShiftAmount <= Max; ++ShiftAmount) { - AB |= (AOut.*Shift)(ShiftAmount); + uint8_t LoopRange = Max - Min; + auto Mask = AOut; + for (unsigned ShiftAmnt = 1; ShiftAmnt <= LoopRange; ShiftAmnt <<= 1) { + APInt Shifted = (Mask.*Shift)(ShiftAmnt); + Mask |= Shifted; } + AB = (Mask.*Shift)(Min); }; switch (UserI->getOpcode()) { @@ -221,13 +225,17 @@ void DemandedBits::determineLiveOperandBits( ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); - // Suppose AOut == 0b0000 1001 + // Suppose AOut == 0b0000 0001 // [min, max] = [1, 3] - // shift by 1 we get 0b0001 0010 - // shift by 2 we get 0b0010 0100 - // shift by 3 we get 0b0100 1000 - // we take the or for every shift to cover all the positions. + // iteration 1 shift by 1 mask is 0b0000 0011 + // iteration 2 shift by 2 mask is 0b0000 1111 + // iteration 3, shiftAmnt = 4 > max - min, we stop. // + // After the iterations we need one more shift by min, + // to move from 0b0000 1111 to --> 0b0001 1110. + // The loop populates the mask relative to (0,...,max-min), + // but we need coverage from (min, max). + // This is why the shift by min is needed. GetShiftedRange(Min, Max, /*ShiftLeft=*/true); if (cast(UserI)->isExact()) AB |= APInt::getLowBitsSet(BitWidth, Max); From c102f648153f363bff0c93f51a74c91500aebb77 Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Tue, 29 Jul 2025 20:14:37 +0300 Subject: [PATCH 08/11] added lambda --- llvm/lib/Analysis/DemandedBits.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 695ae162d134b..2e829fa93db82 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -77,17 +77,17 @@ void DemandedBits::determineLiveOperandBits( } }; auto GetShiftedRange = [&](unsigned Min, unsigned Max, bool ShiftLeft) { - using ShiftFn = APInt (APInt::*)(unsigned) const; - auto Shift = ShiftLeft ? static_cast(&APInt::shl) - : static_cast(&APInt::lshr); + auto ShiftF = [&](const APInt &Mask, unsigned ShiftAmnt) { + return ShiftLeft ? Mask.shl(ShiftAmnt) : Mask.lshr(ShiftAmnt); + }; AB = APInt::getZero(BitWidth); uint8_t LoopRange = Max - Min; auto Mask = AOut; for (unsigned ShiftAmnt = 1; ShiftAmnt <= LoopRange; ShiftAmnt <<= 1) { - APInt Shifted = (Mask.*Shift)(ShiftAmnt); + APInt Shifted = ShiftF(Mask, ShiftAmnt); Mask |= Shifted; } - AB = (Mask.*Shift)(Min); + AB = ShiftF(Mask, Min); }; switch (UserI->getOpcode()) { From bde2923816dabcb4d621e465f5a294953102f7c2 Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Tue, 29 Jul 2025 23:41:28 +0300 Subject: [PATCH 09/11] wrong type fix --- llvm/lib/Analysis/DemandedBits.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 2e829fa93db82..78ab393713a69 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -81,7 +81,7 @@ void DemandedBits::determineLiveOperandBits( return ShiftLeft ? Mask.shl(ShiftAmnt) : Mask.lshr(ShiftAmnt); }; AB = APInt::getZero(BitWidth); - uint8_t LoopRange = Max - Min; + unsigned LoopRange = Max - Min; auto Mask = AOut; for (unsigned ShiftAmnt = 1; ShiftAmnt <= LoopRange; ShiftAmnt <<= 1) { APInt Shifted = ShiftF(Mask, ShiftAmnt); From 27e434a870c2a4b510f8e92bb7c4626856e0f2d5 Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis Date: Wed, 30 Jul 2025 17:01:17 +0300 Subject: [PATCH 10/11] updated types --- llvm/lib/Analysis/DemandedBits.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index 78ab393713a69..bed3a35622248 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -76,13 +76,13 @@ void DemandedBits::determineLiveOperandBits( computeKnownBits(V2, Known2, DL, &AC, UserI, &DT); } }; - auto GetShiftedRange = [&](unsigned Min, unsigned Max, bool ShiftLeft) { - auto ShiftF = [&](const APInt &Mask, unsigned ShiftAmnt) { + auto GetShiftedRange = [&](uint64_t Min, uint64_t Max, bool ShiftLeft) { + auto ShiftF = [ShiftLeft](const APInt &Mask, unsigned ShiftAmnt) { return ShiftLeft ? Mask.shl(ShiftAmnt) : Mask.lshr(ShiftAmnt); }; AB = APInt::getZero(BitWidth); - unsigned LoopRange = Max - Min; - auto Mask = AOut; + uint64_t LoopRange = Max - Min; + APInt Mask = AOut; for (unsigned ShiftAmnt = 1; ShiftAmnt <= LoopRange; ShiftAmnt <<= 1) { APInt Shifted = ShiftF(Mask, ShiftAmnt); Mask |= Shifted; @@ -198,8 +198,8 @@ void DemandedBits::determineLiveOperandBits( AB |= APInt::getHighBitsSet(BitWidth, ShiftAmt); } else { ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); - unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); - unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); + uint64_t Min = Known.getMinValue().getLimitedValue(BitWidth - 1); + uint64_t Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); // similar to Lshr case GetShiftedRange(Min, Max, /*ShiftLeft=*/false); const auto *S = cast(UserI); @@ -223,8 +223,8 @@ void DemandedBits::determineLiveOperandBits( AB |= APInt::getLowBitsSet(BitWidth, ShiftAmt); } else { ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); - unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); - unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); + uint64_t Min = Known.getMinValue().getLimitedValue(BitWidth - 1); + uint64_t Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); // Suppose AOut == 0b0000 0001 // [min, max] = [1, 3] // iteration 1 shift by 1 mask is 0b0000 0011 @@ -261,10 +261,11 @@ void DemandedBits::determineLiveOperandBits( AB |= APInt::getLowBitsSet(BitWidth, ShiftAmt); } else { ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr); - unsigned Min = Known.getMinValue().getLimitedValue(BitWidth - 1); - unsigned Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); + uint64_t Min = Known.getMinValue().getLimitedValue(BitWidth - 1); + uint64_t Max = Known.getMaxValue().getLimitedValue(BitWidth - 1); GetShiftedRange(Min, Max, /*ShiftLeft=*/true); - if (Max) { + if (Max && + (AOut & APInt::getHighBitsSet(BitWidth, Max)).getBoolValue()) { // Suppose AOut = 0011 1100 // [min, max] = [1, 3] // ShiftAmount = 1 : Mask is 1000 0000 @@ -272,8 +273,7 @@ void DemandedBits::determineLiveOperandBits( // ShiftAmount = 3 : Mask is 1110 0000 // The Mask with Max covers every case in [min, max], // so we are done - if ((AOut & APInt::getHighBitsSet(BitWidth, Max)).getBoolValue()) - AB.setSignBit(); + AB.setSignBit(); } // If the shift is exact, then the low bits are not dead // (they must be zero). From e9288bd0dbe9024d5ba71267f16a24264ae7656d Mon Sep 17 00:00:00 2001 From: Panagiotis Karouzakis <45971450+karouzakisp@users.noreply.github.com> Date: Tue, 5 Aug 2025 12:13:25 +0300 Subject: [PATCH 11/11] Looks correct Over-approximation fixed Co-authored-by: Yingwei Zheng --- llvm/lib/Analysis/DemandedBits.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Analysis/DemandedBits.cpp b/llvm/lib/Analysis/DemandedBits.cpp index bed3a35622248..e0881751aef7e 100644 --- a/llvm/lib/Analysis/DemandedBits.cpp +++ b/llvm/lib/Analysis/DemandedBits.cpp @@ -83,9 +83,16 @@ void DemandedBits::determineLiveOperandBits( AB = APInt::getZero(BitWidth); uint64_t LoopRange = Max - Min; APInt Mask = AOut; + APInt Shifted = AOut; // AOut | (AOut << 1) | ... | (AOut << (ShiftAmnt - 1) for (unsigned ShiftAmnt = 1; ShiftAmnt <= LoopRange; ShiftAmnt <<= 1) { - APInt Shifted = ShiftF(Mask, ShiftAmnt); - Mask |= Shifted; + if (LoopRange & ShiftAmnt) { + // Account for (LoopRange - ShiftAmnt, LoopRange] + Mask |= ShiftF(Shifted, LoopRange - ShiftAmnt + 1); + // Clears the low bit. + LoopRange -= ShiftAmnt; + } + // [0, ShiftAmnt) -> [0, ShiftAmnt * 2) + Shifted |= ShiftF(Shifted, ShiftAmnt); } AB = ShiftF(Mask, Min); };