Skip to content

Commit f12a556

Browse files
[InstCombine] Fold binop of select and cast of select condition
Simplify binary operations, whose operands involve a `select` instruction and a cast of the `select` condition. Specifically, the binop is canonicalized into a `select` with folded arguments as follows: (Binop (zext C), (select C, T, F)) -> (select C, (binop 1, T), (binop 0, F)) (Binop (sext C), (select C, T, F)) -> (select C, (binop -1, T), (binop 0, F)) Proofs: https://alive2.llvm.org/ce/z/c_JwwM Differential Revision: https://reviews.llvm.org/D153963
1 parent c9fd7ac commit f12a556

File tree

5 files changed

+98
-45
lines changed

5 files changed

+98
-45
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,9 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
16181618
if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
16191619
return Res;
16201620

1621+
if (Instruction *Res = foldBinOpOfSelectAndCastOfSelectCondition(I))
1622+
return Res;
1623+
16211624
return Changed ? &I : nullptr;
16221625
}
16231626

@@ -2466,6 +2469,9 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
24662469
}
24672470
}
24682471

2472+
if (Instruction *Res = foldBinOpOfSelectAndCastOfSelectCondition(I))
2473+
return Res;
2474+
24692475
return TryToNarrowDeduceFlags();
24702476
}
24712477

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,12 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
454454
// -> (BinOp (logic_shift (BinOp X, Y)), Mask)
455455
Instruction *foldBinOpShiftWithShift(BinaryOperator &I);
456456

457+
/// Tries to simplify binops of select and cast of the select condition.
458+
///
459+
/// (Binop (cast C), (select C, T, F))
460+
/// -> (select C, C0, C1)
461+
Instruction *foldBinOpOfSelectAndCastOfSelectCondition(BinaryOperator &I);
462+
457463
/// This tries to simplify binary operations by factorizing out common terms
458464
/// (e. g. "(A*B)+(A*C)" -> "A*(B+C)").
459465
Value *tryFactorizationFolds(BinaryOperator &I);

llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,9 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
474474
if (Instruction *Ext = narrowMathIfNoOverflow(I))
475475
return Ext;
476476

477+
if (Instruction *Res = foldBinOpOfSelectAndCastOfSelectCondition(I))
478+
return Res;
479+
477480
// min(X, Y) * max(X, Y) => X * Y.
478481
if (match(&I, m_CombineOr(m_c_Mul(m_SMax(m_Value(X), m_Value(Y)),
479482
m_c_SMin(m_Deferred(X), m_Deferred(Y))),

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,71 @@ Instruction *InstCombinerImpl::foldBinOpShiftWithShift(BinaryOperator &I) {
870870
return MatchBinOp(1);
871871
}
872872

873+
// (Binop (zext C), (select C, T, F))
874+
// -> (select C, (binop 1, T), (binop 0, F))
875+
//
876+
// (Binop (sext C), (select C, T, F))
877+
// -> (select C, (binop -1, T), (binop 0, F))
878+
//
879+
// Attempt to simplify binary operations into a select with folded args, when
880+
// one operand of the binop is a select instruction and the other operand is a
881+
// zext/sext extension, whose value is the select condition.
882+
Instruction *
883+
InstCombinerImpl::foldBinOpOfSelectAndCastOfSelectCondition(BinaryOperator &I) {
884+
// TODO: this simplification may be extended to any speculatable instruction,
885+
// not just binops, and would possibly be handled better in FoldOpIntoSelect.
886+
Instruction::BinaryOps Opc = I.getOpcode();
887+
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
888+
Value *A, *CondVal, *TrueVal, *FalseVal;
889+
Value *CastOp;
890+
891+
auto MatchSelectAndCast = [&](Value *CastOp, Value *SelectOp) {
892+
return match(CastOp, m_ZExtOrSExt(m_Value(A))) &&
893+
A->getType()->getScalarSizeInBits() == 1 &&
894+
match(SelectOp, m_Select(m_Value(CondVal), m_Value(TrueVal),
895+
m_Value(FalseVal)));
896+
};
897+
898+
// Make sure one side of the binop is a select instruction, and the other is a
899+
// zero/sign extension operating on a i1.
900+
if (MatchSelectAndCast(LHS, RHS))
901+
CastOp = LHS;
902+
else if (MatchSelectAndCast(RHS, LHS))
903+
CastOp = RHS;
904+
else
905+
return nullptr;
906+
907+
auto NewFoldedConst = [&](bool IsTrueArm, Value *V) {
908+
bool IsCastOpRHS = (CastOp == RHS);
909+
bool IsZExt = isa<ZExtInst>(CastOp);
910+
Constant *C;
911+
912+
if (IsTrueArm) {
913+
C = Constant::getNullValue(V->getType());
914+
} else if (IsZExt) {
915+
C = Constant::getIntegerValue(
916+
V->getType(), APInt(V->getType()->getIntegerBitWidth(), 1));
917+
} else {
918+
C = Constant::getAllOnesValue(V->getType());
919+
}
920+
921+
return IsCastOpRHS ? Builder.CreateBinOp(Opc, V, C)
922+
: Builder.CreateBinOp(Opc, C, V);
923+
};
924+
925+
// If the value used in the zext/sext is the select condition, or the negated
926+
// of the select condition, the binop can be simplified.
927+
if (CondVal == A)
928+
return SelectInst::Create(CondVal, NewFoldedConst(false, TrueVal),
929+
NewFoldedConst(true, FalseVal));
930+
931+
if (match(A, m_Not(m_Specific(CondVal))))
932+
return SelectInst::Create(CondVal, NewFoldedConst(true, TrueVal),
933+
NewFoldedConst(false, FalseVal));
934+
935+
return nullptr;
936+
}
937+
873938
Value *InstCombinerImpl::tryFactorizationFolds(BinaryOperator &I) {
874939
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
875940
BinaryOperator *Op0 = dyn_cast<BinaryOperator>(LHS);

llvm/test/Transforms/InstCombine/binop-select-cast-of-select-cond.ll

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
define i64 @add_select_zext(i1 %c) {
55
; CHECK-LABEL: define i64 @add_select_zext
66
; CHECK-SAME: (i1 [[C:%.*]]) {
7-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 64, i64 1
8-
; CHECK-NEXT: [[EXT:%.*]] = zext i1 [[C]] to i64
9-
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[SEL]], [[EXT]]
7+
; CHECK-NEXT: [[ADD:%.*]] = select i1 [[C]], i64 65, i64 1
108
; CHECK-NEXT: ret i64 [[ADD]]
119
;
1210
%sel = select i1 %c, i64 64, i64 1
@@ -18,9 +16,7 @@ define i64 @add_select_zext(i1 %c) {
1816
define i64 @add_select_sext(i1 %c) {
1917
; CHECK-LABEL: define i64 @add_select_sext
2018
; CHECK-SAME: (i1 [[C:%.*]]) {
21-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 64, i64 1
22-
; CHECK-NEXT: [[EXT:%.*]] = sext i1 [[C]] to i64
23-
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[SEL]], [[EXT]]
19+
; CHECK-NEXT: [[ADD:%.*]] = select i1 [[C]], i64 63, i64 1
2420
; CHECK-NEXT: ret i64 [[ADD]]
2521
;
2622
%sel = select i1 %c, i64 64, i64 1
@@ -32,10 +28,7 @@ define i64 @add_select_sext(i1 %c) {
3228
define i64 @add_select_not_zext(i1 %c) {
3329
; CHECK-LABEL: define i64 @add_select_not_zext
3430
; CHECK-SAME: (i1 [[C:%.*]]) {
35-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 64, i64 1
36-
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C]], true
37-
; CHECK-NEXT: [[EXT:%.*]] = zext i1 [[NOT_C]] to i64
38-
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[SEL]], [[EXT]]
31+
; CHECK-NEXT: [[ADD:%.*]] = select i1 [[C]], i64 64, i64 2
3932
; CHECK-NEXT: ret i64 [[ADD]]
4033
;
4134
%sel = select i1 %c, i64 64, i64 1
@@ -48,10 +41,7 @@ define i64 @add_select_not_zext(i1 %c) {
4841
define i64 @add_select_not_sext(i1 %c) {
4942
; CHECK-LABEL: define i64 @add_select_not_sext
5043
; CHECK-SAME: (i1 [[C:%.*]]) {
51-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 64, i64 1
52-
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C]], true
53-
; CHECK-NEXT: [[EXT:%.*]] = sext i1 [[NOT_C]] to i64
54-
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[SEL]], [[EXT]]
44+
; CHECK-NEXT: [[ADD:%.*]] = select i1 [[C]], i64 64, i64 0
5545
; CHECK-NEXT: ret i64 [[ADD]]
5646
;
5747
%sel = select i1 %c, i64 64, i64 1
@@ -64,9 +54,7 @@ define i64 @add_select_not_sext(i1 %c) {
6454
define i64 @sub_select_sext(i1 %c, i64 %arg) {
6555
; CHECK-LABEL: define i64 @sub_select_sext
6656
; CHECK-SAME: (i1 [[C:%.*]], i64 [[ARG:%.*]]) {
67-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 64, i64 [[ARG]]
68-
; CHECK-NEXT: [[EXT_NEG:%.*]] = zext i1 [[C]] to i64
69-
; CHECK-NEXT: [[SUB:%.*]] = add i64 [[SEL]], [[EXT_NEG]]
57+
; CHECK-NEXT: [[SUB:%.*]] = select i1 [[C]], i64 65, i64 [[ARG]]
7058
; CHECK-NEXT: ret i64 [[SUB]]
7159
;
7260
%sel = select i1 %c, i64 64, i64 %arg
@@ -78,10 +66,7 @@ define i64 @sub_select_sext(i1 %c, i64 %arg) {
7866
define i64 @sub_select_not_zext(i1 %c, i64 %arg) {
7967
; CHECK-LABEL: define i64 @sub_select_not_zext
8068
; CHECK-SAME: (i1 [[C:%.*]], i64 [[ARG:%.*]]) {
81-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 [[ARG]], i64 64
82-
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C]], true
83-
; CHECK-NEXT: [[EXT_NEG:%.*]] = sext i1 [[NOT_C]] to i64
84-
; CHECK-NEXT: [[SUB:%.*]] = add i64 [[SEL]], [[EXT_NEG]]
69+
; CHECK-NEXT: [[SUB:%.*]] = select i1 [[C]], i64 [[ARG]], i64 63
8570
; CHECK-NEXT: ret i64 [[SUB]]
8671
;
8772
%sel = select i1 %c, i64 %arg, i64 64
@@ -94,10 +79,7 @@ define i64 @sub_select_not_zext(i1 %c, i64 %arg) {
9479
define i64 @sub_select_not_sext(i1 %c, i64 %arg) {
9580
; CHECK-LABEL: define i64 @sub_select_not_sext
9681
; CHECK-SAME: (i1 [[C:%.*]], i64 [[ARG:%.*]]) {
97-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 [[ARG]], i64 64
98-
; CHECK-NEXT: [[NOT_C:%.*]] = xor i1 [[C]], true
99-
; CHECK-NEXT: [[EXT_NEG:%.*]] = zext i1 [[NOT_C]] to i64
100-
; CHECK-NEXT: [[SUB:%.*]] = add i64 [[SEL]], [[EXT_NEG]]
82+
; CHECK-NEXT: [[SUB:%.*]] = select i1 [[C]], i64 [[ARG]], i64 65
10183
; CHECK-NEXT: ret i64 [[SUB]]
10284
;
10385
%sel = select i1 %c, i64 %arg, i64 64
@@ -122,9 +104,7 @@ define i64 @mul_select_zext(i1 %c, i64 %arg) {
122104
define i64 @mul_select_sext(i1 %c) {
123105
; CHECK-LABEL: define i64 @mul_select_sext
124106
; CHECK-SAME: (i1 [[C:%.*]]) {
125-
; CHECK-NEXT: [[EXT:%.*]] = sext i1 [[C]] to i64
126-
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[C]], i64 6, i64 0
127-
; CHECK-NEXT: [[MUL:%.*]] = shl i64 [[EXT]], [[TMP1]]
107+
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[C]], i64 -64, i64 0
128108
; CHECK-NEXT: ret i64 [[MUL]]
129109
;
130110
%sel = select i1 %c, i64 64, i64 1
@@ -168,10 +148,7 @@ define <2 x i64> @vector_test(i1 %c) {
168148
define i64 @multiuse_add(i1 %c) {
169149
; CHECK-LABEL: define i64 @multiuse_add
170150
; CHECK-SAME: (i1 [[C:%.*]]) {
171-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 64, i64 1
172-
; CHECK-NEXT: [[EXT:%.*]] = zext i1 [[C]] to i64
173-
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[SEL]], [[EXT]]
174-
; CHECK-NEXT: [[ADD2:%.*]] = add nuw nsw i64 [[ADD]], 1
151+
; CHECK-NEXT: [[ADD2:%.*]] = select i1 [[C]], i64 66, i64 2
175152
; CHECK-NEXT: ret i64 [[ADD2]]
176153
;
177154
%sel = select i1 %c, i64 64, i64 1
@@ -184,10 +161,7 @@ define i64 @multiuse_add(i1 %c) {
184161
define i64 @multiuse_select(i1 %c) {
185162
; CHECK-LABEL: define i64 @multiuse_select
186163
; CHECK-SAME: (i1 [[C:%.*]]) {
187-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 64, i64 0
188-
; CHECK-NEXT: [[EXT_NEG:%.*]] = sext i1 [[C]] to i64
189-
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[SEL]], [[EXT_NEG]]
190-
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i64 [[SEL]], [[ADD]]
164+
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[C]], i64 4032, i64 0
191165
; CHECK-NEXT: ret i64 [[MUL]]
192166
;
193167
%sel = select i1 %c, i64 64, i64 0
@@ -200,9 +174,8 @@ define i64 @multiuse_select(i1 %c) {
200174
define i64 @select_non_const_sides(i1 %c, i64 %arg1, i64 %arg2) {
201175
; CHECK-LABEL: define i64 @select_non_const_sides
202176
; CHECK-SAME: (i1 [[C:%.*]], i64 [[ARG1:%.*]], i64 [[ARG2:%.*]]) {
203-
; CHECK-NEXT: [[EXT_NEG:%.*]] = sext i1 [[C]] to i64
204-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i64 [[ARG1]], i64 [[ARG2]]
205-
; CHECK-NEXT: [[SUB:%.*]] = add i64 [[SEL]], [[EXT_NEG]]
177+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[ARG1]], -1
178+
; CHECK-NEXT: [[SUB:%.*]] = select i1 [[C]], i64 [[TMP1]], i64 [[ARG2]]
206179
; CHECK-NEXT: ret i64 [[SUB]]
207180
;
208181
%ext = zext i1 %c to i64
@@ -214,9 +187,9 @@ define i64 @select_non_const_sides(i1 %c, i64 %arg1, i64 %arg2) {
214187
define i6 @sub_select_sext_op_swapped_non_const_args(i1 %c, i6 %argT, i6 %argF) {
215188
; CHECK-LABEL: define i6 @sub_select_sext_op_swapped_non_const_args
216189
; CHECK-SAME: (i1 [[C:%.*]], i6 [[ARGT:%.*]], i6 [[ARGF:%.*]]) {
217-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i6 [[ARGT]], i6 [[ARGF]]
218-
; CHECK-NEXT: [[EXT:%.*]] = sext i1 [[C]] to i6
219-
; CHECK-NEXT: [[SUB:%.*]] = sub i6 [[EXT]], [[SEL]]
190+
; CHECK-DAG: [[TMP1:%.*]] = xor i6 [[ARGT]], -1
191+
; CHECK-DAG: [[TMP2:%.*]] = sub i6 0, [[ARGF]]
192+
; CHECK-NEXT: [[SUB:%.*]] = select i1 [[C]], i6 [[TMP1]], i6 [[TMP2]]
220193
; CHECK-NEXT: ret i6 [[SUB]]
221194
;
222195
%sel = select i1 %c, i6 %argT, i6 %argF
@@ -228,9 +201,9 @@ define i6 @sub_select_sext_op_swapped_non_const_args(i1 %c, i6 %argT, i6 %argF)
228201
define i6 @sub_select_zext_op_swapped_non_const_args(i1 %c, i6 %argT, i6 %argF) {
229202
; CHECK-LABEL: define i6 @sub_select_zext_op_swapped_non_const_args
230203
; CHECK-SAME: (i1 [[C:%.*]], i6 [[ARGT:%.*]], i6 [[ARGF:%.*]]) {
231-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], i6 [[ARGT]], i6 [[ARGF]]
232-
; CHECK-NEXT: [[EXT:%.*]] = zext i1 [[C]] to i6
233-
; CHECK-NEXT: [[SUB:%.*]] = sub i6 [[EXT]], [[SEL]]
204+
; CHECK-DAG: [[TMP1:%.*]] = sub i6 1, [[ARGT]]
205+
; CHECK-DAG: [[TMP2:%.*]] = sub i6 0, [[ARGF]]
206+
; CHECK-NEXT: [[SUB:%.*]] = select i1 [[C]], i6 [[TMP1]], i6 [[TMP2]]
234207
; CHECK-NEXT: ret i6 [[SUB]]
235208
;
236209
%sel = select i1 %c, i6 %argT, i6 %argF

0 commit comments

Comments
 (0)