[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