Skip to content

Commit 79b0d21

Browse files
committed
[InstCombine] Fold (~x) & y --> ~(x | (~y)) iff it is free to do so
Iff we know we can get rid of the inversions in the new pattern, we can thus get rid of the inversion in the old pattern, this decreasing instruction count.
1 parent 6260490 commit 79b0d21

File tree

3 files changed

+50
-8
lines changed

3 files changed

+50
-8
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,10 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
19831983
return SelectInst::Create(NewICmpInst, X, ConstantInt::getNullValue(Ty));
19841984
}
19851985

1986+
// (~x) & y --> ~(x | (~y)) iff that gets rid of inversions
1987+
if (sinkNotIntoOtherHandOfAnd(I))
1988+
return &I;
1989+
19861990
return nullptr;
19871991
}
19881992

@@ -3089,6 +3093,45 @@ static Instruction *sinkNotIntoXor(BinaryOperator &I,
30893093
return BinaryOperator::CreateXor(NotX, Y, I.getName() + ".demorgan");
30903094
}
30913095

3096+
// Transform
3097+
// z = (~x) & y
3098+
// into:
3099+
// z = ~(x | (~y))
3100+
// iff y is free to invert and all uses of z can be freely updated.
3101+
bool InstCombinerImpl::sinkNotIntoOtherHandOfAnd(BinaryOperator &I) {
3102+
Instruction::BinaryOps NewOpc;
3103+
switch (I.getOpcode()) {
3104+
case Instruction::And:
3105+
NewOpc = Instruction::Or;
3106+
break;
3107+
default:
3108+
return false;
3109+
};
3110+
3111+
Value *X, *Y;
3112+
if (!match(&I, m_c_BinOp(m_Not(m_Value(X)), m_Value(Y))))
3113+
return false;
3114+
3115+
// Will we be able to fold the `not` into Y eventually?
3116+
if (!InstCombiner::isFreeToInvert(Y, Y->hasOneUse()))
3117+
return false;
3118+
3119+
// And can our users be adapted?
3120+
if (!InstCombiner::canFreelyInvertAllUsersOf(&I, /*IgnoredUser=*/nullptr))
3121+
return false;
3122+
3123+
Value *NotY = Builder.CreateNot(Y, Y->getName() + ".not");
3124+
Value *NewBinOp =
3125+
BinaryOperator::Create(NewOpc, X, NotY, I.getName() + ".not");
3126+
Builder.Insert(NewBinOp);
3127+
replaceInstUsesWith(I, NewBinOp);
3128+
// We can not just create an outer `not`, it will most likely be immediately
3129+
// folded back, reconstructing our initial pattern, and causing an
3130+
// infinite combine loop, so immediately manually fold it away.
3131+
freelyInvertAllUsersOf(NewBinOp);
3132+
return true;
3133+
}
3134+
30923135
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
30933136
// here. We should standardize that construct where it is needed or choose some
30943137
// other way to ensure that commutated variants of patterns are not missed.

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
105105
Value *simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1, bool Inverted);
106106
Instruction *visitAnd(BinaryOperator &I);
107107
Instruction *visitOr(BinaryOperator &I);
108+
bool sinkNotIntoOtherHandOfAnd(BinaryOperator &I);
108109
Instruction *visitXor(BinaryOperator &I);
109110
Instruction *visitShl(BinaryOperator &I);
110111
Value *reassociateShiftAmtsOfTwoSameDirectionShifts(

llvm/test/Transforms/InstCombine/sink-not-into-another-hand-of-and.ll

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ declare void @use1(i1)
1212
; Most basic positive test
1313
define i32 @t0(i1 %i0, i32 %v0, i32 %v1, i32 %v2, i32 %v3) {
1414
; CHECK-LABEL: @t0(
15-
; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]]
16-
; CHECK-NEXT: [[I2:%.*]] = xor i1 [[I0:%.*]], true
17-
; CHECK-NEXT: [[I3:%.*]] = and i1 [[I1]], [[I2]]
18-
; CHECK-NEXT: [[I4:%.*]] = select i1 [[I3]], i32 [[V2:%.*]], i32 [[V3:%.*]]
15+
; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]]
16+
; CHECK-NEXT: [[TMP1:%.*]] = or i1 [[I1]], [[I0:%.*]]
17+
; CHECK-NEXT: [[I4:%.*]] = select i1 [[TMP1]], i32 [[V3:%.*]], i32 [[V2:%.*]]
1918
; CHECK-NEXT: ret i32 [[I4]]
2019
;
2120
%i1 = icmp eq i32 %v0, %v1
@@ -27,11 +26,10 @@ define i32 @t0(i1 %i0, i32 %v0, i32 %v1, i32 %v2, i32 %v3) {
2726
define i32 @t1(i32 %v0, i32 %v1, i32 %v2, i32 %v3, i32 %v4, i32 %v5) {
2827
; CHECK-LABEL: @t1(
2928
; CHECK-NEXT: [[I0:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]]
30-
; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]]
29+
; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]]
3130
; CHECK-NEXT: call void @use1(i1 [[I0]])
32-
; CHECK-NEXT: [[I2:%.*]] = xor i1 [[I0]], true
33-
; CHECK-NEXT: [[I3:%.*]] = and i1 [[I1]], [[I2]]
34-
; CHECK-NEXT: [[I4:%.*]] = select i1 [[I3]], i32 [[V4:%.*]], i32 [[V5:%.*]]
31+
; CHECK-NEXT: [[TMP1:%.*]] = or i1 [[I0]], [[I1]]
32+
; CHECK-NEXT: [[I4:%.*]] = select i1 [[TMP1]], i32 [[V5:%.*]], i32 [[V4:%.*]]
3533
; CHECK-NEXT: ret i32 [[I4]]
3634
;
3735
%i0 = icmp eq i32 %v0, %v1

0 commit comments

Comments
 (0)