Skip to content

Commit

Permalink
[InstCombine] Canonicalize "and, add", "or, add", "xor, add"
Browse files Browse the repository at this point in the history
Canonicalize
```
((x + C1) & C2) --> ((x & C2) + C1)
((x + C1) ^ C2) --> ((x ^ C2) + C1)
((x + C1) | C2) --> ((x | C2) + C1)
```
for suitable constants `C1` and `C2`.

Alive2 proofs: [[ https://alive2.llvm.org/ce/z/BqMDVZ | add, or --> or, add ]]
[[ https://alive2.llvm.org/ce/z/BhAeCl | add, xor --> xor, add ]]
[[ https://alive2.llvm.org/ce/z/jYRHEt | add, and --> and, add ]]

Reviewed By: spatel

Differential Revision: https://reviews.llvm.org/D131142
  • Loading branch information
emgullufsen committed Aug 26, 2022
1 parent a310637 commit eb1e2b3
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 25 deletions.
60 changes: 52 additions & 8 deletions llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
Expand Up @@ -1767,6 +1767,49 @@ static Instruction *reassociateForUses(BinaryOperator &BO,
return nullptr;
}

// Match
// (X + C2) | C
// (X + C2) ^ C
// (X + C2) & C
// and convert to do the bitwise logic first:
// (X | C) + C2
// (X ^ C) + C2
// (X & C) + C2
// iff bits affected by logic op are lower than last bit affected by math op
static Instruction *canonicalizeLogicFirst(BinaryOperator &I,
InstCombiner::BuilderTy &Builder) {
Type *Ty = I.getType();
Instruction::BinaryOps OpC = I.getOpcode();
Value *Op0 = I.getOperand(0);
Value *Op1 = I.getOperand(1);
Value *X;
const APInt *C, *C2;

if (!(match(Op0, m_OneUse(m_Add(m_Value(X), m_APInt(C2)))) &&
match(Op1, m_APInt(C))))
return nullptr;

unsigned Width = Ty->getScalarSizeInBits();
unsigned LastOneMath = Width - C2->countTrailingZeros();

switch (OpC) {
case Instruction::And:
if (C->countLeadingOnes() < LastOneMath)
return nullptr;
break;
case Instruction::Xor:
case Instruction::Or:
if (C->countLeadingZeros() < LastOneMath)
return nullptr;
break;
default:
llvm_unreachable("Unexpected BinaryOp!");
}

Value *NewBinOp = Builder.CreateBinOp(OpC, X, ConstantInt::get(Ty, *C));
return BinaryOperator::CreateAdd(NewBinOp, ConstantInt::get(Ty, *C2));
}

// 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.
Expand Down Expand Up @@ -1888,14 +1931,6 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
Value *NewAnd = Builder.CreateAnd(X, Op1);
return BinaryOperator::CreateXor(NewAnd, Op1);
}

// If all bits affected by the add are included in a high-bit-mask, do the
// mask op before the add. Example:
// (X + 16) & -4 --> (X & -4) + 16
if (Op0->hasOneUse() && C->isNegatedPowerOf2() && *AddC == (*AddC & *C)) {
Value *NewAnd = Builder.CreateAnd(X, Op1);
return BinaryOperator::CreateAdd(NewAnd, ConstantInt::get(Ty, *AddC));
}
}

// ((C1 OP zext(X)) & C2) -> zext((C1 OP X) & C2) if C2 fits in the
Expand Down Expand Up @@ -2238,6 +2273,9 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
if (Instruction *R = reassociateForUses(I, Builder))
return R;

if (Instruction *Canonicalized = canonicalizeLogicFirst(I, Builder))
return Canonicalized;

return nullptr;
}

Expand Down Expand Up @@ -3199,6 +3237,9 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
if (Instruction *R = reassociateForUses(I, Builder))
return R;

if (Instruction *Canonicalized = canonicalizeLogicFirst(I, Builder))
return Canonicalized;

return nullptr;
}

Expand Down Expand Up @@ -3939,5 +3980,8 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
if (Instruction *R = reassociateForUses(I, Builder))
return R;

if (Instruction *Canonicalized = canonicalizeLogicFirst(I, Builder))
return Canonicalized;

return nullptr;
}
24 changes: 12 additions & 12 deletions llvm/test/Transforms/InstCombine/and-xor-or.ll
Expand Up @@ -4358,8 +4358,8 @@ define i1 @PR56294(i8 %x) {
define i32 @canonicalize_logic_first_or0(i32 %x) {
; CHECK-LABEL: define {{[^@]+}}@canonicalize_logic_first_or0
; CHECK-SAME: (i32 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = add i32 [[X]], 112
; CHECK-NEXT: [[R:%.*]] = or i32 [[A]], 15
; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[X]], 15
; CHECK-NEXT: [[R:%.*]] = add i32 [[TMP1]], 112
; CHECK-NEXT: ret i32 [[R]]
;
%a = add i32 %x, 112 ; 01110000
Expand All @@ -4370,8 +4370,8 @@ define i32 @canonicalize_logic_first_or0(i32 %x) {
define <2 x i32> @canonicalize_logic_first_or_vector0(<2 x i32> %x) {
; CHECK-LABEL: define {{[^@]+}}@canonicalize_logic_first_or_vector0
; CHECK-SAME: (<2 x i32> [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = add <2 x i32> [[X]], <i32 112, i32 112>
; CHECK-NEXT: [[R:%.*]] = or <2 x i32> [[A]], <i32 15, i32 15>
; CHECK-NEXT: [[TMP1:%.*]] = or <2 x i32> [[X]], <i32 15, i32 15>
; CHECK-NEXT: [[R:%.*]] = add <2 x i32> [[TMP1]], <i32 112, i32 112>
; CHECK-NEXT: ret <2 x i32> [[R]]
;
%a = add <2 x i32> <i32 112, i32 112>, %x ; <0x00000070, 0x00000070>
Expand Down Expand Up @@ -4433,8 +4433,8 @@ define i32 @canonicalize_logic_first_or_bad_constraints2(i32 %x) {
define i8 @canonicalize_logic_first_and0(i8 %x) {
; CHECK-LABEL: define {{[^@]+}}@canonicalize_logic_first_and0
; CHECK-SAME: (i8 [[X:%.*]]) {
; CHECK-NEXT: [[B:%.*]] = add i8 [[X]], 48
; CHECK-NEXT: [[R:%.*]] = and i8 [[B]], -10
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -10
; CHECK-NEXT: [[R:%.*]] = add i8 [[TMP1]], 48
; CHECK-NEXT: ret i8 [[R]]
;
%b = add i8 %x, 48 ; 00110000
Expand All @@ -4445,8 +4445,8 @@ define i8 @canonicalize_logic_first_and0(i8 %x) {
define <2 x i8> @canonicalize_logic_first_and_vector0(<2 x i8> %x) {
; CHECK-LABEL: define {{[^@]+}}@canonicalize_logic_first_and_vector0
; CHECK-SAME: (<2 x i8> [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = add <2 x i8> [[X]], <i8 48, i8 48>
; CHECK-NEXT: [[R:%.*]] = and <2 x i8> [[A]], <i8 -10, i8 -10>
; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i8> [[X]], <i8 -10, i8 -10>
; CHECK-NEXT: [[R:%.*]] = add <2 x i8> [[TMP1]], <i8 48, i8 48>
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%a = add <2 x i8> <i8 48, i8 48>, %x
Expand Down Expand Up @@ -4522,8 +4522,8 @@ define i8 @canonicalize_logic_first_and_bad_constraints2(i8 %x) {
define i8 @canonicalize_logic_first_xor_0(i8 %x) {
; CHECK-LABEL: define {{[^@]+}}@canonicalize_logic_first_xor_0
; CHECK-SAME: (i8 [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = add i8 [[X]], 96
; CHECK-NEXT: [[R:%.*]] = xor i8 [[A]], 31
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], 31
; CHECK-NEXT: [[R:%.*]] = add i8 [[TMP1]], 96
; CHECK-NEXT: ret i8 [[R]]
;
%a = add i8 %x, 96 ; 01100000
Expand All @@ -4534,8 +4534,8 @@ define i8 @canonicalize_logic_first_xor_0(i8 %x) {
define <2 x i32> @canonicalize_logic_first_xor_vector0(<2 x i32> %x) {
; CHECK-LABEL: define {{[^@]+}}@canonicalize_logic_first_xor_vector0
; CHECK-SAME: (<2 x i32> [[X:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = add <2 x i32> [[X]], <i32 -8388608, i32 -8388608>
; CHECK-NEXT: [[R:%.*]] = xor <2 x i32> [[A]], <i32 32783, i32 32783>
; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i32> [[X]], <i32 32783, i32 32783>
; CHECK-NEXT: [[R:%.*]] = add <2 x i32> [[TMP1]], <i32 -8388608, i32 -8388608>
; CHECK-NEXT: ret <2 x i32> [[R]]
;
%a = add <2 x i32> <i32 -8388608, i32 -8388608>, %x ; <0xFF800000, 0xFF800000>
Expand Down
3 changes: 1 addition & 2 deletions llvm/test/Transforms/InstCombine/freeze.ll
Expand Up @@ -132,8 +132,7 @@ define i32 @early_freeze_test3(i32 %v1) {
; CHECK-LABEL: @early_freeze_test3(
; CHECK-NEXT: [[V1_FR:%.*]] = freeze i32 [[V1:%.*]]
; CHECK-NEXT: [[V2:%.*]] = shl i32 [[V1_FR]], 1
; CHECK-NEXT: [[V3:%.*]] = add i32 [[V2]], 2
; CHECK-NEXT: [[V4:%.*]] = or i32 [[V3]], 1
; CHECK-NEXT: [[V4:%.*]] = add i32 [[V2]], 3
; CHECK-NEXT: ret i32 [[V4]]
;
%v2 = shl i32 %v1, 1
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/Transforms/InstCombine/sub.ll
Expand Up @@ -1517,10 +1517,10 @@ define i8 @sub_not_mask_lowbits(i8 %x) {

define <2 x i8> @sub_mask_lowbits_splat_extra_use(<2 x i8> %x, <2 x i8>* %p) {
; CHECK-LABEL: @sub_mask_lowbits_splat_extra_use(
; CHECK-NEXT: [[A1:%.*]] = add <2 x i8> [[X:%.*]], <i8 -64, i8 -64>
; CHECK-NEXT: [[A2:%.*]] = and <2 x i8> [[X]], <i8 10, i8 10>
; CHECK-NEXT: [[A2:%.*]] = and <2 x i8> [[X:%.*]], <i8 10, i8 10>
; CHECK-NEXT: store <2 x i8> [[A2]], <2 x i8>* [[P:%.*]], align 2
; CHECK-NEXT: [[R:%.*]] = and <2 x i8> [[A1]], <i8 -11, i8 -11>
; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i8> [[X]], <i8 -11, i8 -11>
; CHECK-NEXT: [[R:%.*]] = add <2 x i8> [[TMP1]], <i8 -64, i8 -64>
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%a1 = add <2 x i8> %x, <i8 192, i8 192> ; 0xc0
Expand Down

0 comments on commit eb1e2b3

Please sign in to comment.