Skip to content

Commit 0441df9

Browse files
committed
[InstCombine,InstSimplify] Optimize select followed by and/or/xor
This patch adds `A & (A && B)` -> `A && B` (similarly for or + logical or) Also, this patch adds `~(select C, (icmp pred X, Y), const)` -> `select C, (icmp pred' X, Y), ~const`. Alive2 proof: merge_and: https://alive2.llvm.org/ce/z/teMR97 merge_or: https://alive2.llvm.org/ce/z/b4yZUp xor_and: https://alive2.llvm.org/ce/z/_-TXHi xor_or: https://alive2.llvm.org/ce/z/2uYx_a Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D94861
1 parent 14573d4 commit 0441df9

File tree

3 files changed

+68
-43
lines changed

3 files changed

+68
-43
lines changed

llvm/lib/Analysis/InstructionSimplify.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,12 +2127,21 @@ static Value *SimplifyAndInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
21272127
Instruction::Xor, Q, MaxRecurse))
21282128
return V;
21292129

2130-
// If the operation is with the result of a select instruction, check whether
2131-
// operating on either branch of the select always yields the same value.
2132-
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1))
2130+
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1)) {
2131+
if (Op0->getType()->isIntOrIntVectorTy(1)) {
2132+
// A & (A && B) -> A && B
2133+
if (match(Op1, m_Select(m_Specific(Op0), m_Value(), m_Zero())))
2134+
return Op1;
2135+
else if (match(Op0, m_Select(m_Specific(Op1), m_Value(), m_Zero())))
2136+
return Op0;
2137+
}
2138+
// If the operation is with the result of a select instruction, check
2139+
// whether operating on either branch of the select always yields the same
2140+
// value.
21332141
if (Value *V = ThreadBinOpOverSelect(Instruction::And, Op0, Op1, Q,
21342142
MaxRecurse))
21352143
return V;
2144+
}
21362145

21372146
// If the operation is with the result of a phi instruction, check whether
21382147
// operating on all incoming values of the phi always yields the same value.
@@ -2303,12 +2312,21 @@ static Value *SimplifyOrInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
23032312
Instruction::And, Q, MaxRecurse))
23042313
return V;
23052314

2306-
// If the operation is with the result of a select instruction, check whether
2307-
// operating on either branch of the select always yields the same value.
2308-
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1))
2315+
if (isa<SelectInst>(Op0) || isa<SelectInst>(Op1)) {
2316+
if (Op0->getType()->isIntOrIntVectorTy(1)) {
2317+
// A | (A || B) -> A || B
2318+
if (match(Op1, m_Select(m_Specific(Op0), m_One(), m_Value())))
2319+
return Op1;
2320+
else if (match(Op0, m_Select(m_Specific(Op1), m_One(), m_Value())))
2321+
return Op0;
2322+
}
2323+
// If the operation is with the result of a select instruction, check
2324+
// whether operating on either branch of the select always yields the same
2325+
// value.
23092326
if (Value *V = ThreadBinOpOverSelect(Instruction::Or, Op0, Op1, Q,
23102327
MaxRecurse))
23112328
return V;
2329+
}
23122330

23132331
// (A & C1)|(B & C2)
23142332
const APInt *C1, *C2;

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3444,19 +3444,32 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
34443444
}
34453445
}
34463446

3447-
// Pull 'not' into operands of select if both operands are one-use compares.
3447+
// Pull 'not' into operands of select if both operands are one-use compares
3448+
// or one is one-use compare and the other one is a constant.
34483449
// Inverting the predicates eliminates the 'not' operation.
34493450
// Example:
3450-
// not (select ?, (cmp TPred, ?, ?), (cmp FPred, ?, ?) -->
3451+
// not (select ?, (cmp TPred, ?, ?), (cmp FPred, ?, ?) -->
34513452
// select ?, (cmp InvTPred, ?, ?), (cmp InvFPred, ?, ?)
3452-
// TODO: Canonicalize by hoisting 'not' into an arm of the select if only
3453-
// 1 select operand is a cmp?
3453+
// not (select ?, (cmp TPred, ?, ?), true -->
3454+
// select ?, (cmp InvTPred, ?, ?), false
34543455
if (auto *Sel = dyn_cast<SelectInst>(Op0)) {
3455-
auto *CmpT = dyn_cast<CmpInst>(Sel->getTrueValue());
3456-
auto *CmpF = dyn_cast<CmpInst>(Sel->getFalseValue());
3457-
if (CmpT && CmpF && CmpT->hasOneUse() && CmpF->hasOneUse()) {
3458-
CmpT->setPredicate(CmpT->getInversePredicate());
3459-
CmpF->setPredicate(CmpF->getInversePredicate());
3456+
Value *TV = Sel->getTrueValue();
3457+
Value *FV = Sel->getFalseValue();
3458+
auto *CmpT = dyn_cast<CmpInst>(TV);
3459+
auto *CmpF = dyn_cast<CmpInst>(FV);
3460+
bool InvertibleT = (CmpT && CmpT->hasOneUse()) || isa<Constant>(TV);
3461+
bool InvertibleF = (CmpF && CmpF->hasOneUse()) || isa<Constant>(FV);
3462+
if (InvertibleT && InvertibleF) {
3463+
Constant *One = cast<Constant>(Op1);
3464+
3465+
if (CmpT)
3466+
CmpT->setPredicate(CmpT->getInversePredicate());
3467+
else
3468+
Sel->setTrueValue(ConstantExpr::getNot(cast<Constant>(TV)));
3469+
if (CmpF)
3470+
CmpF->setPredicate(CmpF->getInversePredicate());
3471+
else
3472+
Sel->setFalseValue(ConstantExpr::getNot(cast<Constant>(FV)));
34603473
return replaceInstUsesWith(I, Sel);
34613474
}
34623475
}

llvm/test/Transforms/InstCombine/select-safe-transforms.ll

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ define i1 @cond_eq_or_const(i8 %X, i8 %Y) {
5656
define i1 @merge_and(i1 %X, i1 %Y) {
5757
; CHECK-LABEL: @merge_and(
5858
; CHECK-NEXT: [[C:%.*]] = select i1 [[X:%.*]], i1 [[Y:%.*]], i1 false
59-
; CHECK-NEXT: [[RES:%.*]] = and i1 [[C]], [[X]]
60-
; CHECK-NEXT: ret i1 [[RES]]
59+
; CHECK-NEXT: ret i1 [[C]]
6160
;
6261
%c = select i1 %X, i1 %Y, i1 false
6362
%res = and i1 %X, %c
@@ -67,8 +66,7 @@ define i1 @merge_and(i1 %X, i1 %Y) {
6766
define i1 @merge_or(i1 %X, i1 %Y) {
6867
; CHECK-LABEL: @merge_or(
6968
; CHECK-NEXT: [[C:%.*]] = select i1 [[X:%.*]], i1 true, i1 [[Y:%.*]]
70-
; CHECK-NEXT: [[RES:%.*]] = or i1 [[C]], [[X]]
71-
; CHECK-NEXT: ret i1 [[RES]]
69+
; CHECK-NEXT: ret i1 [[C]]
7270
;
7371
%c = select i1 %X, i1 true, i1 %Y
7472
%res = or i1 %X, %c
@@ -77,10 +75,10 @@ define i1 @merge_or(i1 %X, i1 %Y) {
7775

7876
define i1 @xor_and(i1 %c, i32 %X, i32 %Y) {
7977
; CHECK-LABEL: @xor_and(
80-
; CHECK-NEXT: [[COMP:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
81-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i1 [[COMP]], i1 false
82-
; CHECK-NEXT: [[RES:%.*]] = xor i1 [[SEL]], true
83-
; CHECK-NEXT: ret i1 [[RES]]
78+
; CHECK-NEXT: [[COMP:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]]
79+
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C:%.*]], true
80+
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 true, i1 [[COMP]]
81+
; CHECK-NEXT: ret i1 [[SEL]]
8482
;
8583
%comp = icmp ult i32 %X, %Y
8684
%sel = select i1 %c, i1 %comp, i1 false
@@ -90,10 +88,9 @@ define i1 @xor_and(i1 %c, i32 %X, i32 %Y) {
9088

9189
define <2 x i1> @xor_and2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
9290
; CHECK-LABEL: @xor_and2(
93-
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
94-
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 true, i1 false>
95-
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
96-
; CHECK-NEXT: ret <2 x i1> [[RES]]
91+
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
92+
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 false, i1 true>
93+
; CHECK-NEXT: ret <2 x i1> [[SEL]]
9794
;
9895
%comp = icmp ult <2 x i32> %X, %Y
9996
%sel = select <2 x i1> %c, <2 x i1> %comp, <2 x i1> <i1 true, i1 false>
@@ -105,10 +102,9 @@ define <2 x i1> @xor_and2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
105102

106103
define <2 x i1> @xor_and3(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
107104
; CHECK-LABEL: @xor_and3(
108-
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
109-
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 icmp eq (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 false>
110-
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
111-
; CHECK-NEXT: ret <2 x i1> [[RES]]
105+
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
106+
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> [[COMP]], <2 x i1> <i1 icmp ne (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 true>
107+
; CHECK-NEXT: ret <2 x i1> [[SEL]]
112108
;
113109
%comp = icmp ult <2 x i32> %X, %Y
114110
%sel = select <2 x i1> %c, <2 x i1> %comp, <2 x i1> <i1 icmp eq (i8* @glb, i8* inttoptr (i64 1234 to i8*)), i1 false>
@@ -118,10 +114,10 @@ define <2 x i1> @xor_and3(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
118114

119115
define i1 @xor_or(i1 %c, i32 %X, i32 %Y) {
120116
; CHECK-LABEL: @xor_or(
121-
; CHECK-NEXT: [[COMP:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
122-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i1 true, i1 [[COMP]]
123-
; CHECK-NEXT: [[RES:%.*]] = xor i1 [[SEL]], true
124-
; CHECK-NEXT: ret i1 [[RES]]
117+
; CHECK-NEXT: [[COMP:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]]
118+
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C:%.*]], true
119+
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[NOT_C]], i1 [[COMP]], i1 false
120+
; CHECK-NEXT: ret i1 [[SEL]]
125121
;
126122
%comp = icmp ult i32 %X, %Y
127123
%sel = select i1 %c, i1 true, i1 %comp
@@ -131,10 +127,9 @@ define i1 @xor_or(i1 %c, i32 %X, i32 %Y) {
131127

132128
define <2 x i1> @xor_or2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
133129
; CHECK-LABEL: @xor_or2(
134-
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
135-
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 true, i1 false>, <2 x i1> [[COMP]]
136-
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
137-
; CHECK-NEXT: ret <2 x i1> [[RES]]
130+
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
131+
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 false, i1 true>, <2 x i1> [[COMP]]
132+
; CHECK-NEXT: ret <2 x i1> [[SEL]]
138133
;
139134
%comp = icmp ult <2 x i32> %X, %Y
140135
%sel = select <2 x i1> %c, <2 x i1> <i1 true, i1 false>, <2 x i1> %comp
@@ -144,10 +139,9 @@ define <2 x i1> @xor_or2(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
144139

145140
define <2 x i1> @xor_or3(<2 x i1> %c, <2 x i32> %X, <2 x i32> %Y) {
146141
; CHECK-LABEL: @xor_or3(
147-
; CHECK-NEXT: [[COMP:%.*]] = icmp ult <2 x i32> [[X:%.*]], [[Y:%.*]]
148-
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 icmp eq (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 false>, <2 x i1> [[COMP]]
149-
; CHECK-NEXT: [[RES:%.*]] = xor <2 x i1> [[SEL]], <i1 true, i1 true>
150-
; CHECK-NEXT: ret <2 x i1> [[RES]]
142+
; CHECK-NEXT: [[COMP:%.*]] = icmp uge <2 x i32> [[X:%.*]], [[Y:%.*]]
143+
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i1> <i1 icmp ne (i8* inttoptr (i64 1234 to i8*), i8* @glb), i1 true>, <2 x i1> [[COMP]]
144+
; CHECK-NEXT: ret <2 x i1> [[SEL]]
151145
;
152146
%comp = icmp ult <2 x i32> %X, %Y
153147
%sel = select <2 x i1> %c, <2 x i1> <i1 icmp eq (i8* @glb, i8* inttoptr (i64 1234 to i8*)), i1 false>, <2 x i1> %comp

0 commit comments

Comments
 (0)