[InstSimplify] (x | y) & (x | !y) --> x

https://alive2.llvm.org/ce/z/QagQMn

This fold is handled by instcombine via SimplifyUsingDistributiveLaws(),
but we are missing the sibliing fold for 'logical and' (implemented with
'select'). Retrofitting the code in instcombine looks much harder
than just adding a small adjustment here, and this is potentially more
efficient and beneficial to other passes.
This commit is contained in:
Sanjay Patel
2021-10-06 11:32:46 -04:00
parent 4666324f2b
commit e36d351d19
2 changed files with 54 additions and 42 deletions

View File

@@ -2042,11 +2042,19 @@ static Value *SimplifyAndInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
if (match(Op1, m_c_Or(m_Specific(Op0), m_Value())))
return Op0;
// (X | Y) & (X | ~Y) --> X (commuted 8 ways)
Value *X, *Y;
if (match(Op0, m_c_Or(m_Value(X), m_Not(m_Value(Y)))) &&
match(Op1, m_c_Or(m_Deferred(X), m_Deferred(Y))))
return X;
if (match(Op1, m_c_Or(m_Value(X), m_Not(m_Value(Y)))) &&
match(Op0, m_c_Or(m_Deferred(X), m_Deferred(Y))))
return X;
if (Value *V = simplifyLogicOfAddSub(Op0, Op1, Instruction::And))
return V;
// A mask that only clears known zeros of a shifted value is a no-op.
Value *X;
const APInt *Mask;
const APInt *ShAmt;
if (match(Op1, m_APInt(Mask))) {
@@ -2143,7 +2151,7 @@ static Value *SimplifyAndInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
// if Mask = ((1 << effective_width_of(X)) - 1) << A
// SimplifyDemandedBits in InstCombine can optimize the general case.
// This pattern aims to help other passes for a common case.
Value *Y, *XShifted;
Value *XShifted;
if (match(Op1, m_APInt(Mask)) &&
match(Op0, m_c_Or(m_CombineAnd(m_NUWShl(m_Value(X), m_APInt(ShAmt)),
m_Value(XShifted)),

View File

@@ -9,13 +9,11 @@ define i32 @poison(i32 %x) {
ret i32 %v
}
; (X | Y) & (X | ~Y) --> X (commuted 8 ways)
define i8 @or_or_not_commute0(i8 %x, i8 %y) {
; CHECK-LABEL: @or_or_not_commute0(
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XORY:%.*]] = or i8 [[X:%.*]], [[Y]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[X]], [[YNOT]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORY]], [[XORYNOT]]
; CHECK-NEXT: ret i8 [[AND]]
; CHECK-NEXT: ret i8 [[X:%.*]]
;
%ynot = xor i8 %y, -1
%xory = or i8 %x, %y
@@ -26,11 +24,7 @@ define i8 @or_or_not_commute0(i8 %x, i8 %y) {
define <2 x i5> @or_or_not_commute1(<2 x i5> %x, <2 x i5> %y) {
; CHECK-LABEL: @or_or_not_commute1(
; CHECK-NEXT: [[YNOT:%.*]] = xor <2 x i5> [[Y:%.*]], <i5 -1, i5 -1>
; CHECK-NEXT: [[XORY:%.*]] = or <2 x i5> [[X:%.*]], [[Y]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or <2 x i5> [[X]], [[YNOT]]
; CHECK-NEXT: [[AND:%.*]] = and <2 x i5> [[XORYNOT]], [[XORY]]
; CHECK-NEXT: ret <2 x i5> [[AND]]
; CHECK-NEXT: ret <2 x i5> [[X:%.*]]
;
%ynot = xor <2 x i5> %y, <i5 -1, i5 -1>
%xory = or <2 x i5> %x, %y
@@ -41,11 +35,7 @@ define <2 x i5> @or_or_not_commute1(<2 x i5> %x, <2 x i5> %y) {
define <2 x i8> @or_or_not_commute2(<2 x i8> %x, <2 x i8> %y) {
; CHECK-LABEL: @or_or_not_commute2(
; CHECK-NEXT: [[YNOT:%.*]] = xor <2 x i8> [[Y:%.*]], <i8 poison, i8 -1>
; CHECK-NEXT: [[XORY:%.*]] = or <2 x i8> [[X:%.*]], [[Y]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or <2 x i8> [[YNOT]], [[X]]
; CHECK-NEXT: [[AND:%.*]] = and <2 x i8> [[XORY]], [[XORYNOT]]
; CHECK-NEXT: ret <2 x i8> [[AND]]
; CHECK-NEXT: ret <2 x i8> [[X:%.*]]
;
%ynot = xor <2 x i8> %y, <i8 poison, i8 -1>
%xory = or <2 x i8> %x, %y
@@ -56,11 +46,7 @@ define <2 x i8> @or_or_not_commute2(<2 x i8> %x, <2 x i8> %y) {
define i8 @or_or_not_commute3(i8 %x, i8 %y) {
; CHECK-LABEL: @or_or_not_commute3(
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XORY:%.*]] = or i8 [[X:%.*]], [[Y]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[YNOT]], [[X]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORYNOT]], [[XORY]]
; CHECK-NEXT: ret i8 [[AND]]
; CHECK-NEXT: ret i8 [[X:%.*]]
;
%ynot = xor i8 %y, -1
%xory = or i8 %x, %y
@@ -70,11 +56,7 @@ define i8 @or_or_not_commute3(i8 %x, i8 %y) {
}
define i8 @or_or_not_commute4(i8 %x, i8 %y) {
; CHECK-LABEL: @or_or_not_commute4(
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XORY:%.*]] = or i8 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[X]], [[YNOT]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORY]], [[XORYNOT]]
; CHECK-NEXT: ret i8 [[AND]]
; CHECK-NEXT: ret i8 [[X:%.*]]
;
%ynot = xor i8 %y, -1
%xory = or i8 %y, %x
@@ -85,11 +67,7 @@ define i8 @or_or_not_commute4(i8 %x, i8 %y) {
define i8 @or_or_not_commute5(i8 %x, i8 %y) {
; CHECK-LABEL: @or_or_not_commute5(
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XORY:%.*]] = or i8 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[X]], [[YNOT]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORYNOT]], [[XORY]]
; CHECK-NEXT: ret i8 [[AND]]
; CHECK-NEXT: ret i8 [[X:%.*]]
;
%ynot = xor i8 %y, -1
%xory = or i8 %y, %x
@@ -100,11 +78,7 @@ define i8 @or_or_not_commute5(i8 %x, i8 %y) {
define i8 @or_or_not_commute6(i8 %x, i8 %y) {
; CHECK-LABEL: @or_or_not_commute6(
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XORY:%.*]] = or i8 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[YNOT]], [[X]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORY]], [[XORYNOT]]
; CHECK-NEXT: ret i8 [[AND]]
; CHECK-NEXT: ret i8 [[X:%.*]]
;
%ynot = xor i8 %y, -1
%xory = or i8 %y, %x
@@ -115,11 +89,7 @@ define i8 @or_or_not_commute6(i8 %x, i8 %y) {
define i8 @or_or_not_commute7(i8 %x, i8 %y) {
; CHECK-LABEL: @or_or_not_commute7(
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XORY:%.*]] = or i8 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[YNOT]], [[X]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORYNOT]], [[XORY]]
; CHECK-NEXT: ret i8 [[AND]]
; CHECK-NEXT: ret i8 [[X:%.*]]
;
%ynot = xor i8 %y, -1
%xory = or i8 %y, %x
@@ -127,3 +97,37 @@ define i8 @or_or_not_commute7(i8 %x, i8 %y) {
%and = and i8 %xorynot, %xory
ret i8 %and
}
; negative test - wrong logic op
define i8 @or_xor_not(i8 %x, i8 %y) {
; CHECK-LABEL: @or_xor_not(
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XXORY:%.*]] = xor i8 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[X]], [[YNOT]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORYNOT]], [[XXORY]]
; CHECK-NEXT: ret i8 [[AND]]
;
%ynot = xor i8 %y, -1
%xxory = xor i8 %y, %x
%xorynot = or i8 %x, %ynot
%and = and i8 %xorynot, %xxory
ret i8 %and
}
; negative test - must have common operands
define i8 @or_or_not_no_common_op(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @or_or_not_no_common_op(
; CHECK-NEXT: [[XORZ:%.*]] = or i8 [[Z:%.*]], [[X:%.*]]
; CHECK-NEXT: [[YNOT:%.*]] = xor i8 [[Y:%.*]], -1
; CHECK-NEXT: [[XORYNOT:%.*]] = or i8 [[X]], [[YNOT]]
; CHECK-NEXT: [[AND:%.*]] = and i8 [[XORYNOT]], [[XORZ]]
; CHECK-NEXT: ret i8 [[AND]]
;
%xorz = or i8 %z, %x
%ynot = xor i8 %y, -1
%xorynot = or i8 %x, %ynot
%and = and i8 %xorynot, %xorz
ret i8 %and
}