Skip to content

Commit

Permalink
[InstSimplify] (x | y) & (x | !y) --> x
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rotateright committed Oct 6, 2021
1 parent 4666324 commit e36d351
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 42 deletions.
12 changes: 10 additions & 2 deletions llvm/lib/Analysis/InstructionSimplify.cpp
Expand Up @@ -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))) {
Expand Down Expand Up @@ -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)),
Expand Down
84 changes: 44 additions & 40 deletions llvm/test/Transforms/InstSimplify/and.ll
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -115,15 +89,45 @@ 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
%xorynot = or i8 %ynot, %x
%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
}

0 comments on commit e36d351

Please sign in to comment.