return nullptr;
}
+static Instruction *foldAndToXor(BinaryOperator &I,
+ InstCombiner::BuilderTy &Builder) {
+ assert(I.getOpcode() == Instruction::And);
+ Value *Op0 = I.getOperand(0);
+ Value *Op1 = I.getOperand(1);
+ Value *A, *B;
+
+ // Operand complexity canonicalization guarantees that the 'or' is Op0.
+ // (A | B) & ~(A & B) --> A ^ B
+ // (A | B) & ~(B & A) --> A ^ B
+ if (match(Op0, m_Or(m_Value(A), m_Value(B))) &&
+ match(Op1, m_Not(m_c_And(m_Specific(A), m_Specific(B)))))
+ return BinaryOperator::CreateXor(A, B);
+
+ // (A | ~B) & (~A | B) --> ~(A ^ B)
+ // (A | ~B) & (B | ~A) --> ~(A ^ B)
+ // (~B | A) & (~A | B) --> ~(A ^ B)
+ // (~B | A) & (B | ~A) --> ~(A ^ B)
+ if (match(Op0, m_c_Or(m_Value(A), m_Not(m_Value(B)))) &&
+ match(Op1, m_c_Or(m_Not(m_Specific(A)), m_Value(B))))
+ return BinaryOperator::CreateNot(Builder.CreateXor(A, B));
+
+ return nullptr;
+}
+
+static Instruction *foldOrToXor(BinaryOperator &I,
+ InstCombiner::BuilderTy &Builder) {
+ assert(I.getOpcode() == Instruction::Or);
+ Value *Op0 = I.getOperand(0);
+ Value *Op1 = I.getOperand(1);
+ Value *A, *B;
+
+ // Operand complexity canonicalization guarantees that the 'and' is Op0.
+ // (A & B) | ~(A | B) --> ~(A ^ B)
+ // (A & B) | ~(B | A) --> ~(A ^ B)
+ if (match(Op0, m_And(m_Value(A), m_Value(B))) &&
+ match(Op1, m_Not(m_c_Or(m_Specific(A), m_Specific(B)))))
+ return BinaryOperator::CreateNot(Builder.CreateXor(A, B));
+
+ // (A & ~B) | (~A & B) --> A ^ B
+ // (A & ~B) | (B & ~A) --> A ^ B
+ // (~B & A) | (~A & B) --> A ^ B
+ // (~B & A) | (B & ~A) --> A ^ B
+ if (match(Op0, m_c_And(m_Value(A), m_Not(m_Value(B)))) &&
+ match(Op1, m_c_And(m_Not(m_Specific(A)), m_Specific(B))))
+ return BinaryOperator::CreateXor(A, B);
+
+ return nullptr;
+}
+
// 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.
if (Value *V = SimplifyAndInst(Op0, Op1, DL, &TLI, &DT, &AC))
return replaceInstUsesWith(I, V);
- // (A|B)&(A|C) -> A|(B&C) etc
- if (Value *V = SimplifyUsingDistributiveLaws(I))
- return replaceInstUsesWith(I, V);
-
// See if we can simplify any instructions used by the instruction whose sole
// purpose is to compute bits we don't care about.
if (SimplifyDemandedInstructionBits(I))
return &I;
+ // Do this before using distributive laws to catch simple and/or/not patterns.
+ if (Instruction *Xor = foldAndToXor(I, *Builder))
+ return Xor;
+
+ // (A|B)&(A|C) -> A|(B&C) etc
+ if (Value *V = SimplifyUsingDistributiveLaws(I))
+ return replaceInstUsesWith(I, V);
+
if (Value *V = SimplifyBSwap(I))
return replaceInstUsesWith(I, V);
return DeMorgan;
{
- Value *A = nullptr, *B = nullptr, *C = nullptr, *D = nullptr;
- // (A|B) & ~(A&B) -> A^B
- if (match(Op0, m_Or(m_Value(A), m_Value(B))) &&
- match(Op1, m_Not(m_And(m_Value(C), m_Value(D)))) &&
- ((A == C && B == D) || (A == D && B == C)))
- return BinaryOperator::CreateXor(A, B);
-
- // ~(A&B) & (A|B) -> A^B
- if (match(Op1, m_Or(m_Value(A), m_Value(B))) &&
- match(Op0, m_Not(m_And(m_Value(C), m_Value(D)))) &&
- ((A == C && B == D) || (A == D && B == C)))
- return BinaryOperator::CreateXor(A, B);
-
+ Value *A = nullptr, *B = nullptr, *C = nullptr;
// A&(A^B) => A & ~B
{
Value *tmpOp0 = Op0;
if (Value *V = SimplifyOrInst(Op0, Op1, DL, &TLI, &DT, &AC))
return replaceInstUsesWith(I, V);
- // (A&B)|(A&C) -> A&(B|C) etc
- if (Value *V = SimplifyUsingDistributiveLaws(I))
- return replaceInstUsesWith(I, V);
-
// See if we can simplify any instructions used by the instruction whose sole
// purpose is to compute bits we don't care about.
if (SimplifyDemandedInstructionBits(I))
return &I;
+ // Do this before using distributive laws to catch simple and/or/not patterns.
+ if (Instruction *Xor = foldOrToXor(I, *Builder))
+ return Xor;
+
+ // (A&B)|(A&C) -> A&(B|C) etc
+ if (Value *V = SimplifyUsingDistributiveLaws(I))
+ return replaceInstUsesWith(I, V);
+
if (Value *V = SimplifyBSwap(I))
return replaceInstUsesWith(I, V);
return replaceInstUsesWith(I, V);
}
- // ((A&~B)|(~A&B)) -> A^B
- if ((match(C, m_Not(m_Specific(D))) &&
- match(B, m_Not(m_Specific(A)))))
- return BinaryOperator::CreateXor(A, D);
- // ((~B&A)|(~A&B)) -> A^B
- if ((match(A, m_Not(m_Specific(D))) &&
- match(B, m_Not(m_Specific(C)))))
- return BinaryOperator::CreateXor(C, D);
- // ((A&~B)|(B&~A)) -> A^B
- if ((match(C, m_Not(m_Specific(B))) &&
- match(D, m_Not(m_Specific(A)))))
- return BinaryOperator::CreateXor(A, B);
- // ((~B&A)|(B&~A)) -> A^B
- if ((match(A, m_Not(m_Specific(B))) &&
- match(D, m_Not(m_Specific(C)))))
- return BinaryOperator::CreateXor(C, B);
-
// ((A|B)&1)|(B&-2) -> (A&1) | B
if (match(A, m_Or(m_Value(V1), m_Specific(B))) ||
match(A, m_Or(m_Specific(B), m_Value(V1)))) {
define i32 @and_to_xor3(i32 %a, i32 %b) {
; CHECK-LABEL: @and_to_xor3(
-; CHECK-NEXT: [[AND2:%.*]] = xor i32 %b, %a
+; CHECK-NEXT: [[AND2:%.*]] = xor i32 %a, %b
; CHECK-NEXT: ret i32 [[AND2]]
;
%or = or i32 %a, %b
define i32 @and_to_xor4(i32 %a, i32 %b) {
; CHECK-LABEL: @and_to_xor4(
-; CHECK-NEXT: [[AND2:%.*]] = xor i32 %a, %b
+; CHECK-NEXT: [[AND2:%.*]] = xor i32 %b, %a
; CHECK-NEXT: ret i32 [[AND2]]
;
%or = or i32 %b, %a
; In the next 4 tests, cast instructions are used to thwart operand complexity
; canonicalizations, so we can test all of the commuted patterns.
+; (a | ~b) & (~a | b) --> ~(a ^ b)
+
define i32 @and_to_nxor1(float %fa, float %fb) {
; CHECK-LABEL: @and_to_nxor1(
; CHECK-NEXT: [[A:%.*]] = fptosi float %fa to i32
; CHECK-NEXT: [[B:%.*]] = fptosi float %fb to i32
-; CHECK-NEXT: [[NOTA:%.*]] = xor i32 [[A]], -1
-; CHECK-NEXT: [[NOTB:%.*]] = xor i32 [[B]], -1
-; CHECK-NEXT: [[OR1:%.*]] = or i32 [[A]], [[NOTB]]
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[NOTA]], [[B]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[OR1]], [[OR2]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[A]], [[B]]
+; CHECK-NEXT: [[AND:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[AND]]
;
%a = fptosi float %fa to i32
ret i32 %and
}
+; (a | ~b) & (b | ~a) --> ~(a ^ b)
+
define i32 @and_to_nxor2(float %fa, float %fb) {
; CHECK-LABEL: @and_to_nxor2(
; CHECK-NEXT: [[A:%.*]] = fptosi float %fa to i32
; CHECK-NEXT: [[B:%.*]] = fptosi float %fb to i32
-; CHECK-NEXT: [[NOTA:%.*]] = xor i32 [[A]], -1
-; CHECK-NEXT: [[NOTB:%.*]] = xor i32 [[B]], -1
-; CHECK-NEXT: [[OR1:%.*]] = or i32 [[A]], [[NOTB]]
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[B]], [[NOTA]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[OR1]], [[OR2]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[A]], [[B]]
+; CHECK-NEXT: [[AND:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[AND]]
;
%a = fptosi float %fa to i32
ret i32 %and
}
+; (~a | b) & (a | ~b) --> ~(a ^ b)
+
define i32 @and_to_nxor3(float %fa, float %fb) {
; CHECK-LABEL: @and_to_nxor3(
; CHECK-NEXT: [[A:%.*]] = fptosi float %fa to i32
; CHECK-NEXT: [[B:%.*]] = fptosi float %fb to i32
-; CHECK-NEXT: [[NOTA:%.*]] = xor i32 [[A]], -1
-; CHECK-NEXT: [[NOTB:%.*]] = xor i32 [[B]], -1
-; CHECK-NEXT: [[OR1:%.*]] = or i32 [[NOTA]], [[B]]
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[A]], [[NOTB]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[OR1]], [[OR2]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[B]], [[A]]
+; CHECK-NEXT: [[AND:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[AND]]
;
%a = fptosi float %fa to i32
ret i32 %and
}
+; (~a | b) & (~b | a) --> ~(a ^ b)
+
define i32 @and_to_nxor4(float %fa, float %fb) {
; CHECK-LABEL: @and_to_nxor4(
; CHECK-NEXT: [[A:%.*]] = fptosi float %fa to i32
; CHECK-NEXT: [[B:%.*]] = fptosi float %fb to i32
-; CHECK-NEXT: [[NOTA:%.*]] = xor i32 [[A]], -1
-; CHECK-NEXT: [[NOTB:%.*]] = xor i32 [[B]], -1
-; CHECK-NEXT: [[OR1:%.*]] = or i32 [[NOTA]], [[B]]
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[NOTB]], [[A]]
-; CHECK-NEXT: [[AND:%.*]] = and i32 [[OR1]], [[OR2]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[B]], [[A]]
+; CHECK-NEXT: [[AND:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[AND]]
;
%a = fptosi float %fa to i32
ret i32 %or
}
+; (a & b) | ~(a | b) --> ~(a ^ b)
+
define i32 @or_to_nxor1(i32 %a, i32 %b) {
; CHECK-LABEL: @or_to_nxor1(
-; CHECK-NEXT: [[AND:%.*]] = and i32 %a, %b
-; CHECK-NEXT: [[OR:%.*]] = or i32 %a, %b
-; CHECK-NEXT: [[NOTOR:%.*]] = xor i32 [[OR]], -1
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[AND]], [[NOTOR]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 %a, %b
+; CHECK-NEXT: [[OR2:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[OR2]]
;
%and = and i32 %a, %b
ret i32 %or2
}
+; (a & b) | ~(b | a) --> ~(a ^ b)
+
define i32 @or_to_nxor2(i32 %a, i32 %b) {
; CHECK-LABEL: @or_to_nxor2(
-; CHECK-NEXT: [[AND:%.*]] = and i32 %a, %b
-; CHECK-NEXT: [[OR:%.*]] = or i32 %b, %a
-; CHECK-NEXT: [[NOTOR:%.*]] = xor i32 [[OR]], -1
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[AND]], [[NOTOR]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 %a, %b
+; CHECK-NEXT: [[OR2:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[OR2]]
;
%and = and i32 %a, %b
ret i32 %or2
}
+; ~(a | b) | (a & b) --> ~(a ^ b)
+
define i32 @or_to_nxor3(i32 %a, i32 %b) {
; CHECK-LABEL: @or_to_nxor3(
-; CHECK-NEXT: [[AND:%.*]] = and i32 %a, %b
-; CHECK-NEXT: [[OR:%.*]] = or i32 %a, %b
-; CHECK-NEXT: [[NOTOR:%.*]] = xor i32 [[OR]], -1
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[AND]], [[NOTOR]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 %a, %b
+; CHECK-NEXT: [[OR2:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[OR2]]
;
%and = and i32 %a, %b
ret i32 %or2
}
+; ~(a | b) | (b & a) --> ~(a ^ b)
+
define i32 @or_to_nxor4(i32 %a, i32 %b) {
; CHECK-LABEL: @or_to_nxor4(
-; CHECK-NEXT: [[AND:%.*]] = and i32 %b, %a
-; CHECK-NEXT: [[OR:%.*]] = or i32 %a, %b
-; CHECK-NEXT: [[NOTOR:%.*]] = xor i32 [[OR]], -1
-; CHECK-NEXT: [[OR2:%.*]] = or i32 [[AND]], [[NOTOR]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 %b, %a
+; CHECK-NEXT: [[OR2:%.*]] = xor i32 [[TMP1]], -1
; CHECK-NEXT: ret i32 [[OR2]]
;
%and = and i32 %b, %a