[llvm] b45a73f - [InstCombine] Fold binop of shifts with related amounts

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 28 01:00:32 PDT 2023


Author: Nikita Popov
Date: 2023-06-28T09:54:36+02:00
New Revision: b45a73f4d07424b37395f543974ef1ef6dec6332

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

LOG: [InstCombine] Fold binop of shifts with related amounts

Fold

  binop(shift(ShiftedC1, ShAmt), shift(ShiftedC2, add(ShAmt, AddC)))
    ->
  shift(binop(ShiftedC1, shift(ShiftedC2, AddC)), ShAmt)

where both shifts are the same and AddC is a valid shift amount.

Proofs: https://alive2.llvm.org/ce/z/PhVVeg

Differential Revision: https://reviews.llvm.org/D152927

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
    llvm/lib/Transforms/InstCombine/InstCombineInternal.h
    llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index fb39c40241277..79b5845570b1e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1610,6 +1610,9 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
         I, Builder.CreateIntrinsic(Intrinsic::ctpop, {I.getType()},
                                    {Builder.CreateOr(A, B)}));
 
+  if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
+    return Res;
+
   return Changed ? &I : nullptr;
 }
 

diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 6d0de7601245f..c5c3cad9e5d2e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2117,6 +2117,49 @@ static Instruction *canonicalizeLogicFirst(BinaryOperator &I,
                                                ConstantInt::get(Ty, *C2), Add);
 }
 
+// binop(shift(ShiftedC1, ShAmt), shift(ShiftedC2, add(ShAmt, AddC))) ->
+// shift(binop(ShiftedC1, shift(ShiftedC2, AddC)), ShAmt)
+// where both shifts are the same and AddC is a valid shift amount.
+Instruction *InstCombinerImpl::foldBinOpOfDisplacedShifts(BinaryOperator &I) {
+  assert((I.isBitwiseLogicOp() || I.getOpcode() == Instruction::Add) &&
+         "Unexpected opcode");
+
+  Value *ShAmt;
+  Constant *ShiftedC1, *ShiftedC2, *AddC;
+  Type *Ty = I.getType();
+  unsigned BitWidth = Ty->getScalarSizeInBits();
+  if (!match(&I,
+             m_c_BinOp(m_Shift(m_ImmConstant(ShiftedC1), m_Value(ShAmt)),
+                       m_Shift(m_ImmConstant(ShiftedC2),
+                               m_Add(m_Deferred(ShAmt), m_ImmConstant(AddC))))))
+    return nullptr;
+
+  // Make sure the add constant is a valid shift amount.
+  if (!match(AddC,
+             m_SpecificInt_ICMP(ICmpInst::ICMP_ULT, APInt(BitWidth, BitWidth))))
+    return nullptr;
+
+  // Avoid constant expressions.
+  auto *Op0Inst = dyn_cast<Instruction>(I.getOperand(0));
+  auto *Op1Inst = dyn_cast<Instruction>(I.getOperand(1));
+  if (!Op0Inst || !Op1Inst)
+    return nullptr;
+
+  // Both shifts must be the same.
+  Instruction::BinaryOps ShiftOp =
+      static_cast<Instruction::BinaryOps>(Op0Inst->getOpcode());
+  if (ShiftOp != Op1Inst->getOpcode())
+    return nullptr;
+
+  // For adds, only left shifts are supported.
+  if (I.getOpcode() == Instruction::Add && ShiftOp != Instruction::Shl)
+    return nullptr;
+
+  Value *NewC = Builder.CreateBinOp(
+      I.getOpcode(), ShiftedC1, Builder.CreateBinOp(ShiftOp, ShiftedC2, AddC));
+  return BinaryOperator::Create(ShiftOp, NewC, ShAmt);
+}
+
 // FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
 // here. We should standardize that construct where it is needed or choose some
 // other way to ensure that commutated variants of patterns are not missed.
@@ -2601,6 +2644,9 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   if (Instruction *Folded = foldLogicOfIsFPClass(I, Op0, Op1))
     return Folded;
 
+  if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
+    return Res;
+
   return nullptr;
 }
 
@@ -3626,6 +3672,9 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
   if (Instruction *Folded = foldLogicOfIsFPClass(I, Op0, Op1))
     return Folded;
 
+  if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
+    return Res;
+
   return nullptr;
 }
 
@@ -4543,5 +4592,8 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
   if (Instruction *Folded = canonicalizeConditionalNegationViaMathToSelect(I))
     return Folded;
 
+  if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
+    return Res;
+
   return nullptr;
 }

diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 4cdefba727ac3..426c796c5d8b1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -329,6 +329,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   Instruction *optimizeBitCastFromPhi(CastInst &CI, PHINode *PN);
   Instruction *matchSAddSubSat(IntrinsicInst &MinMax1);
   Instruction *foldNot(BinaryOperator &I);
+  Instruction *foldBinOpOfDisplacedShifts(BinaryOperator &I);
 
   void freelyInvertAllUsersOf(Value *V, Value *IgnoredUser = nullptr);
 

diff  --git a/llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll b/llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll
index ca0047eab6a24..8bfaa23c88bb4 100644
--- a/llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll
+++ b/llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll
@@ -4,10 +4,7 @@
 define i8 @shl_or(i8 %x) {
 ; CHECK-LABEL: define i8 @shl_or
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl i8 16, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl i8 3, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl i8 22, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = shl i8 16, %x
@@ -20,10 +17,7 @@ define i8 @shl_or(i8 %x) {
 define i8 @lshr_or(i8 %x) {
 ; CHECK-LABEL: define i8 @lshr_or
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = lshr i8 16, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = lshr i8 3, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = lshr i8 17, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = lshr i8 16, %x
@@ -36,10 +30,7 @@ define i8 @lshr_or(i8 %x) {
 define i8 @ashr_or(i8 %x) {
 ; CHECK-LABEL: define i8 @ashr_or
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = ashr i8 -64, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = ashr i8 -128, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = ashr i8 -64, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = ashr i8 -64, %x
@@ -52,10 +43,7 @@ define i8 @ashr_or(i8 %x) {
 define i8 @shl_xor(i8 %x) {
 ; CHECK-LABEL: define i8 @shl_xor
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl i8 16, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl i8 3, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = xor i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl i8 22, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = shl i8 16, %x
@@ -68,10 +56,7 @@ define i8 @shl_xor(i8 %x) {
 define i8 @lshr_xor(i8 %x) {
 ; CHECK-LABEL: define i8 @lshr_xor
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = lshr i8 16, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = lshr i8 3, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = xor i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = lshr i8 17, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = lshr i8 16, %x
@@ -84,10 +69,7 @@ define i8 @lshr_xor(i8 %x) {
 define i8 @ashr_xor(i8 %x) {
 ; CHECK-LABEL: define i8 @ashr_xor
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = ashr i8 -128, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = ashr i8 -64, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = xor i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = lshr i8 96, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = ashr i8 -128, %x
@@ -100,10 +82,7 @@ define i8 @ashr_xor(i8 %x) {
 define i8 @shl_and(i8 %x) {
 ; CHECK-LABEL: define i8 @shl_and
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl i8 48, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl i8 8, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = and i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl i8 16, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = shl i8 48, %x
@@ -116,10 +95,7 @@ define i8 @shl_and(i8 %x) {
 define i8 @lshr_and(i8 %x) {
 ; CHECK-LABEL: define i8 @lshr_and
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = lshr i8 48, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = lshr i8 64, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = and i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = lshr i8 32, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = lshr i8 48, %x
@@ -132,10 +108,7 @@ define i8 @lshr_and(i8 %x) {
 define i8 @ashr_and(i8 %x) {
 ; CHECK-LABEL: define i8 @ashr_and
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = ashr i8 -64, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = ashr i8 -128, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = and i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = ashr i8 -64, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = ashr i8 -64, %x
@@ -148,10 +121,7 @@ define i8 @ashr_and(i8 %x) {
 define i8 @shl_add(i8 %x) {
 ; CHECK-LABEL: define i8 @shl_add
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl i8 16, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl i8 7, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = add i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl i8 30, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = shl i8 16, %x
@@ -196,10 +166,7 @@ define i8 @ashr_add_fail(i8 %x) {
 define i8 @shl_or_commuted(i8 %x) {
 ; CHECK-LABEL: define i8 @shl_or_commuted
 ; CHECK-SAME: (i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl i8 16, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl i8 3, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or i8 [[SHIFT2]], [[SHIFT]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl i8 22, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = shl i8 16, %x
@@ -212,10 +179,7 @@ define i8 @shl_or_commuted(i8 %x) {
 define <2 x i8> @shl_or_splat(<2 x i8> %x) {
 ; CHECK-LABEL: define <2 x i8> @shl_or_splat
 ; CHECK-SAME: (<2 x i8> [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl <2 x i8> <i8 16, i8 16>, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i8> [[X]], <i8 1, i8 1>
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl <2 x i8> <i8 3, i8 3>, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl <2 x i8> <i8 22, i8 22>, [[X]]
 ; CHECK-NEXT:    ret <2 x i8> [[BINOP]]
 ;
   %shift = shl <2 x i8> <i8 16, i8 16>, %x
@@ -228,10 +192,7 @@ define <2 x i8> @shl_or_splat(<2 x i8> %x) {
 define <2 x i8> @shl_or_non_splat(<2 x i8> %x) {
 ; CHECK-LABEL: define <2 x i8> @shl_or_non_splat
 ; CHECK-SAME: (<2 x i8> [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl <2 x i8> <i8 16, i8 32>, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i8> [[X]], <i8 1, i8 2>
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl <2 x i8> <i8 3, i8 7>, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl <2 x i8> <i8 22, i8 60>, [[X]]
 ; CHECK-NEXT:    ret <2 x i8> [[BINOP]]
 ;
   %shift = shl <2 x i8> <i8 16, i8 32>, %x
@@ -244,10 +205,7 @@ define <2 x i8> @shl_or_non_splat(<2 x i8> %x) {
 define <2 x i8> @shl_or_undef_in_add(<2 x i8> %x) {
 ; CHECK-LABEL: define <2 x i8> @shl_or_undef_in_add
 ; CHECK-SAME: (<2 x i8> [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl <2 x i8> <i8 16, i8 16>, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i8> [[X]], <i8 1, i8 undef>
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl <2 x i8> <i8 3, i8 3>, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl <2 x i8> <i8 22, i8 poison>, [[X]]
 ; CHECK-NEXT:    ret <2 x i8> [[BINOP]]
 ;
   %shift = shl <2 x i8> <i8 16, i8 16>, %x
@@ -260,10 +218,7 @@ define <2 x i8> @shl_or_undef_in_add(<2 x i8> %x) {
 define <2 x i8> @shl_or_undef_in_shift1(<2 x i8> %x) {
 ; CHECK-LABEL: define <2 x i8> @shl_or_undef_in_shift1
 ; CHECK-SAME: (<2 x i8> [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl <2 x i8> <i8 16, i8 undef>, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i8> [[X]], <i8 1, i8 1>
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl <2 x i8> <i8 3, i8 3>, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl <2 x i8> <i8 22, i8 -1>, [[X]]
 ; CHECK-NEXT:    ret <2 x i8> [[BINOP]]
 ;
   %shift = shl <2 x i8> <i8 16, i8 undef>, %x
@@ -276,10 +231,7 @@ define <2 x i8> @shl_or_undef_in_shift1(<2 x i8> %x) {
 define <2 x i8> @shl_or_undef_in_shift2(<2 x i8> %x) {
 ; CHECK-LABEL: define <2 x i8> @shl_or_undef_in_shift2
 ; CHECK-SAME: (<2 x i8> [[X:%.*]]) {
-; CHECK-NEXT:    [[SHIFT:%.*]] = shl <2 x i8> <i8 16, i8 16>, [[X]]
-; CHECK-NEXT:    [[ADD:%.*]] = add <2 x i8> [[X]], <i8 1, i8 1>
-; CHECK-NEXT:    [[SHIFT2:%.*]] = shl <2 x i8> <i8 3, i8 undef>, [[ADD]]
-; CHECK-NEXT:    [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl <2 x i8> <i8 22, i8 16>, [[X]]
 ; CHECK-NEXT:    ret <2 x i8> [[BINOP]]
 ;
   %shift = shl <2 x i8> <i8 16, i8 16>, %x
@@ -300,7 +252,7 @@ define i8 @shl_or_multiuse(i8 %x) {
 ; CHECK-NEXT:    call void @use(i8 [[SHIFT]])
 ; CHECK-NEXT:    call void @use(i8 [[ADD]])
 ; CHECK-NEXT:    call void @use(i8 [[SHIFT2]])
-; CHECK-NEXT:    [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]]
+; CHECK-NEXT:    [[BINOP:%.*]] = shl i8 22, [[X]]
 ; CHECK-NEXT:    ret i8 [[BINOP]]
 ;
   %shift = shl i8 16, %x


        


More information about the llvm-commits mailing list