Skip to content

Commit 7903ae4

Browse files
committed
[InstCombine] factorize left shifts of add/sub
We do similar factorization folds in SimplifyUsingDistributiveLaws, but that drops no-wrap properties. Propagating those optimally may help solve: https://llvm.org/PR47430 The propagation is all-or-nothing for these patterns: when all 3 incoming ops have nsw or nuw, the 2 new ops should have the same no-wrap property: https://alive2.llvm.org/ce/z/Dv8wsU This also solves: https://llvm.org/PR47584
1 parent cf75e83 commit 7903ae4

File tree

2 files changed

+80
-52
lines changed

2 files changed

+80
-52
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,42 @@ Instruction *InstCombinerImpl::
11991199
return TruncInst::CreateTruncOrBitCast(NewAShr, I.getType());
12001200
}
12011201

1202+
/// This is a specialization of a more general transform from
1203+
/// SimplifyUsingDistributiveLaws. If that code can be made to work optimally
1204+
/// for multi-use cases or propagating nsw/nuw, then we would not need this.
1205+
static Instruction *factorizeMathWithShlOps(BinaryOperator &I,
1206+
InstCombiner::BuilderTy &Builder) {
1207+
// TODO: Also handle mul by doubling the shift amount?
1208+
assert(I.getOpcode() == Instruction::Add ||
1209+
I.getOpcode() == Instruction::Sub && "Expected add/sub");
1210+
auto *Op0 = dyn_cast<BinaryOperator>(I.getOperand(0));
1211+
auto *Op1 = dyn_cast<BinaryOperator>(I.getOperand(1));
1212+
if (!Op0 || !Op1 || !(Op0->hasOneUse() || Op1->hasOneUse()))
1213+
return nullptr;
1214+
1215+
Value *X, *Y, *ShAmt;
1216+
if (!match(Op0, m_Shl(m_Value(X), m_Value(ShAmt))) ||
1217+
!match(Op1, m_Shl(m_Value(Y), m_Specific(ShAmt))))
1218+
return nullptr;
1219+
1220+
// No-wrap propagates only when all ops have no-wrap.
1221+
bool HasNSW = I.hasNoSignedWrap() && Op0->hasNoSignedWrap() &&
1222+
Op1->hasNoSignedWrap();
1223+
bool HasNUW = I.hasNoUnsignedWrap() && Op0->hasNoUnsignedWrap() &&
1224+
Op1->hasNoUnsignedWrap();
1225+
1226+
// add/sub (X << ShAmt), (Y << ShAmt) --> (add/sub X, Y) << ShAmt
1227+
Value *NewMath = Builder.CreateBinOp(I.getOpcode(), X, Y);
1228+
if (auto *NewI = dyn_cast<BinaryOperator>(NewMath)) {
1229+
NewI->setHasNoSignedWrap(HasNSW);
1230+
NewI->setHasNoUnsignedWrap(HasNUW);
1231+
}
1232+
auto *NewShl = BinaryOperator::CreateShl(NewMath, ShAmt);
1233+
NewShl->setHasNoSignedWrap(HasNSW);
1234+
NewShl->setHasNoUnsignedWrap(HasNUW);
1235+
return NewShl;
1236+
}
1237+
12021238
Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
12031239
if (Value *V = SimplifyAddInst(I.getOperand(0), I.getOperand(1),
12041240
I.hasNoSignedWrap(), I.hasNoUnsignedWrap(),
@@ -1215,6 +1251,9 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
12151251
if (Value *V = SimplifyUsingDistributiveLaws(I))
12161252
return replaceInstUsesWith(I, V);
12171253

1254+
if (Instruction *R = factorizeMathWithShlOps(I, Builder))
1255+
return R;
1256+
12181257
if (Instruction *X = foldAddWithConstant(I))
12191258
return X;
12201259

@@ -1753,6 +1792,9 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
17531792
if (Value *V = SimplifyUsingDistributiveLaws(I))
17541793
return replaceInstUsesWith(I, V);
17551794

1795+
if (Instruction *R = factorizeMathWithShlOps(I, Builder))
1796+
return R;
1797+
17561798
if (I.getType()->isIntOrIntVectorTy(1))
17571799
return BinaryOperator::CreateXor(Op0, Op1);
17581800

llvm/test/Transforms/InstCombine/shl-factor.ll

Lines changed: 38 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ declare void @use8(i8)
55

66
define i6 @add_shl_same_amount(i6 %x, i6 %y, i6 %z) {
77
; CHECK-LABEL: @add_shl_same_amount(
8-
; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
9-
; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
10-
; CHECK-NEXT: [[DIFF:%.*]] = add i6 [[XS]], [[YS]]
8+
; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
9+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
1110
; CHECK-NEXT: ret i6 [[DIFF]]
1211
;
1312
%xs = shl i6 %x, %z
@@ -18,9 +17,8 @@ define i6 @add_shl_same_amount(i6 %x, i6 %y, i6 %z) {
1817

1918
define <2 x i4> @add_shl_same_amount_nsw(<2 x i4> %x, <2 x i4> %y, <2 x i4> %z) {
2019
; CHECK-LABEL: @add_shl_same_amount_nsw(
21-
; CHECK-NEXT: [[XS:%.*]] = shl nsw <2 x i4> [[X:%.*]], [[Z:%.*]]
22-
; CHECK-NEXT: [[YS:%.*]] = shl nsw <2 x i4> [[Y:%.*]], [[Z]]
23-
; CHECK-NEXT: [[DIFF:%.*]] = add nsw <2 x i4> [[XS]], [[YS]]
20+
; CHECK-NEXT: [[TMP1:%.*]] = add nsw <2 x i4> [[X:%.*]], [[Y:%.*]]
21+
; CHECK-NEXT: [[DIFF:%.*]] = shl nsw <2 x i4> [[TMP1]], [[Z:%.*]]
2422
; CHECK-NEXT: ret <2 x i4> [[DIFF]]
2523
;
2624
%xs = shl nsw <2 x i4> %x, %z
@@ -31,9 +29,8 @@ define <2 x i4> @add_shl_same_amount_nsw(<2 x i4> %x, <2 x i4> %y, <2 x i4> %z)
3129

3230
define i64 @add_shl_same_amount_nuw(i64 %x, i64 %y, i64 %z) {
3331
; CHECK-LABEL: @add_shl_same_amount_nuw(
34-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i64 [[X:%.*]], [[Z:%.*]]
35-
; CHECK-NEXT: [[YS:%.*]] = shl nuw i64 [[Y:%.*]], [[Z]]
36-
; CHECK-NEXT: [[DIFF:%.*]] = add nuw i64 [[XS]], [[YS]]
32+
; CHECK-NEXT: [[TMP1:%.*]] = add nuw i64 [[X:%.*]], [[Y:%.*]]
33+
; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i64 [[TMP1]], [[Z:%.*]]
3734
; CHECK-NEXT: ret i64 [[DIFF]]
3835
;
3936
%xs = shl nuw i64 %x, %z
@@ -46,8 +43,8 @@ define i8 @add_shl_same_amount_nsw_extra_use1(i8 %x, i8 %y, i8 %z) {
4643
; CHECK-LABEL: @add_shl_same_amount_nsw_extra_use1(
4744
; CHECK-NEXT: [[XS:%.*]] = shl nuw nsw i8 [[X:%.*]], [[Z:%.*]]
4845
; CHECK-NEXT: call void @use8(i8 [[XS]])
49-
; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
50-
; CHECK-NEXT: [[DIFF:%.*]] = add nsw i8 [[XS]], [[YS]]
46+
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i8 [[X]], [[Y:%.*]]
47+
; CHECK-NEXT: [[DIFF:%.*]] = shl nsw i8 [[TMP1]], [[Z]]
5148
; CHECK-NEXT: ret i8 [[DIFF]]
5249
;
5350
%xs = shl nsw nuw i8 %x, %z
@@ -59,10 +56,10 @@ define i8 @add_shl_same_amount_nsw_extra_use1(i8 %x, i8 %y, i8 %z) {
5956

6057
define i8 @add_shl_same_amount_nuw_extra_use2(i8 %x, i8 %y, i8 %z) {
6158
; CHECK-LABEL: @add_shl_same_amount_nuw_extra_use2(
62-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i8 [[X:%.*]], [[Z:%.*]]
63-
; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
59+
; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z:%.*]]
6460
; CHECK-NEXT: call void @use8(i8 [[YS]])
65-
; CHECK-NEXT: [[DIFF:%.*]] = add nuw nsw i8 [[XS]], [[YS]]
61+
; CHECK-NEXT: [[TMP1:%.*]] = add nuw i8 [[X:%.*]], [[Y]]
62+
; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i8 [[TMP1]], [[Z]]
6663
; CHECK-NEXT: ret i8 [[DIFF]]
6764
;
6865
%xs = shl nuw i8 %x, %z
@@ -91,9 +88,8 @@ define i8 @add_shl_same_amount_nsw_nuw_extra_use3(i8 %x, i8 %y, i8 %z) {
9188

9289
define i6 @add_shl_same_amount_partial_nsw1(i6 %x, i6 %y, i6 %z) {
9390
; CHECK-LABEL: @add_shl_same_amount_partial_nsw1(
94-
; CHECK-NEXT: [[XS:%.*]] = shl nsw i6 [[X:%.*]], [[Z:%.*]]
95-
; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
96-
; CHECK-NEXT: [[DIFF:%.*]] = add i6 [[XS]], [[YS]]
91+
; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
92+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
9793
; CHECK-NEXT: ret i6 [[DIFF]]
9894
;
9995
%xs = shl nsw i6 %x, %z
@@ -104,9 +100,8 @@ define i6 @add_shl_same_amount_partial_nsw1(i6 %x, i6 %y, i6 %z) {
104100

105101
define i6 @add_shl_same_amount_partial_nsw2(i6 %x, i6 %y, i6 %z) {
106102
; CHECK-LABEL: @add_shl_same_amount_partial_nsw2(
107-
; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
108-
; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
109-
; CHECK-NEXT: [[DIFF:%.*]] = add nsw i6 [[XS]], [[YS]]
103+
; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
104+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
110105
; CHECK-NEXT: ret i6 [[DIFF]]
111106
;
112107
%xs = shl i6 %x, %z
@@ -117,9 +112,8 @@ define i6 @add_shl_same_amount_partial_nsw2(i6 %x, i6 %y, i6 %z) {
117112

118113
define i6 @add_shl_same_amount_partial_nuw1(i6 %x, i6 %y, i6 %z) {
119114
; CHECK-LABEL: @add_shl_same_amount_partial_nuw1(
120-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
121-
; CHECK-NEXT: [[YS:%.*]] = shl nuw i6 [[Y:%.*]], [[Z]]
122-
; CHECK-NEXT: [[DIFF:%.*]] = add i6 [[XS]], [[YS]]
115+
; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
116+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
123117
; CHECK-NEXT: ret i6 [[DIFF]]
124118
;
125119
%xs = shl nuw i6 %x, %z
@@ -130,9 +124,8 @@ define i6 @add_shl_same_amount_partial_nuw1(i6 %x, i6 %y, i6 %z) {
130124

131125
define i6 @add_shl_same_amount_partial_nuw2(i6 %x, i6 %y, i6 %z) {
132126
; CHECK-LABEL: @add_shl_same_amount_partial_nuw2(
133-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
134-
; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
135-
; CHECK-NEXT: [[DIFF:%.*]] = add nuw i6 [[XS]], [[YS]]
127+
; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
128+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
136129
; CHECK-NEXT: ret i6 [[DIFF]]
137130
;
138131
%xs = shl nuw i6 %x, %z
@@ -143,9 +136,8 @@ define i6 @add_shl_same_amount_partial_nuw2(i6 %x, i6 %y, i6 %z) {
143136

144137
define i6 @sub_shl_same_amount(i6 %x, i6 %y, i6 %z) {
145138
; CHECK-LABEL: @sub_shl_same_amount(
146-
; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
147-
; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
148-
; CHECK-NEXT: [[DIFF:%.*]] = sub i6 [[XS]], [[YS]]
139+
; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
140+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
149141
; CHECK-NEXT: ret i6 [[DIFF]]
150142
;
151143
%xs = shl i6 %x, %z
@@ -156,9 +148,8 @@ define i6 @sub_shl_same_amount(i6 %x, i6 %y, i6 %z) {
156148

157149
define <2 x i4> @sub_shl_same_amount_nsw(<2 x i4> %x, <2 x i4> %y, <2 x i4> %z) {
158150
; CHECK-LABEL: @sub_shl_same_amount_nsw(
159-
; CHECK-NEXT: [[XS:%.*]] = shl nsw <2 x i4> [[X:%.*]], [[Z:%.*]]
160-
; CHECK-NEXT: [[YS:%.*]] = shl nsw <2 x i4> [[Y:%.*]], [[Z]]
161-
; CHECK-NEXT: [[DIFF:%.*]] = sub nsw <2 x i4> [[XS]], [[YS]]
151+
; CHECK-NEXT: [[TMP1:%.*]] = sub nsw <2 x i4> [[X:%.*]], [[Y:%.*]]
152+
; CHECK-NEXT: [[DIFF:%.*]] = shl nsw <2 x i4> [[TMP1]], [[Z:%.*]]
162153
; CHECK-NEXT: ret <2 x i4> [[DIFF]]
163154
;
164155
%xs = shl nsw <2 x i4> %x, %z
@@ -169,9 +160,8 @@ define <2 x i4> @sub_shl_same_amount_nsw(<2 x i4> %x, <2 x i4> %y, <2 x i4> %z)
169160

170161
define i64 @sub_shl_same_amount_nuw(i64 %x, i64 %y, i64 %z) {
171162
; CHECK-LABEL: @sub_shl_same_amount_nuw(
172-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i64 [[X:%.*]], [[Z:%.*]]
173-
; CHECK-NEXT: [[YS:%.*]] = shl nuw i64 [[Y:%.*]], [[Z]]
174-
; CHECK-NEXT: [[DIFF:%.*]] = sub nuw i64 [[XS]], [[YS]]
163+
; CHECK-NEXT: [[TMP1:%.*]] = sub nuw i64 [[X:%.*]], [[Y:%.*]]
164+
; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i64 [[TMP1]], [[Z:%.*]]
175165
; CHECK-NEXT: ret i64 [[DIFF]]
176166
;
177167
%xs = shl nuw i64 %x, %z
@@ -184,8 +174,8 @@ define i8 @sub_shl_same_amount_nsw_extra_use1(i8 %x, i8 %y, i8 %z) {
184174
; CHECK-LABEL: @sub_shl_same_amount_nsw_extra_use1(
185175
; CHECK-NEXT: [[XS:%.*]] = shl nuw nsw i8 [[X:%.*]], [[Z:%.*]]
186176
; CHECK-NEXT: call void @use8(i8 [[XS]])
187-
; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
188-
; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i8 [[XS]], [[YS]]
177+
; CHECK-NEXT: [[TMP1:%.*]] = sub nsw i8 [[X]], [[Y:%.*]]
178+
; CHECK-NEXT: [[DIFF:%.*]] = shl nsw i8 [[TMP1]], [[Z]]
189179
; CHECK-NEXT: ret i8 [[DIFF]]
190180
;
191181
%xs = shl nsw nuw i8 %x, %z
@@ -197,10 +187,10 @@ define i8 @sub_shl_same_amount_nsw_extra_use1(i8 %x, i8 %y, i8 %z) {
197187

198188
define i8 @sub_shl_same_amount_nuw_extra_use2(i8 %x, i8 %y, i8 %z) {
199189
; CHECK-LABEL: @sub_shl_same_amount_nuw_extra_use2(
200-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i8 [[X:%.*]], [[Z:%.*]]
201-
; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
190+
; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z:%.*]]
202191
; CHECK-NEXT: call void @use8(i8 [[YS]])
203-
; CHECK-NEXT: [[DIFF:%.*]] = sub nuw nsw i8 [[XS]], [[YS]]
192+
; CHECK-NEXT: [[TMP1:%.*]] = sub nuw i8 [[X:%.*]], [[Y]]
193+
; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i8 [[TMP1]], [[Z]]
204194
; CHECK-NEXT: ret i8 [[DIFF]]
205195
;
206196
%xs = shl nuw i8 %x, %z
@@ -229,9 +219,8 @@ define i8 @sub_shl_same_amount_nsw_nuw_extra_use3(i8 %x, i8 %y, i8 %z) {
229219

230220
define i6 @sub_shl_same_amount_partial_nsw1(i6 %x, i6 %y, i6 %z) {
231221
; CHECK-LABEL: @sub_shl_same_amount_partial_nsw1(
232-
; CHECK-NEXT: [[XS:%.*]] = shl nsw i6 [[X:%.*]], [[Z:%.*]]
233-
; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
234-
; CHECK-NEXT: [[DIFF:%.*]] = sub i6 [[XS]], [[YS]]
222+
; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
223+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
235224
; CHECK-NEXT: ret i6 [[DIFF]]
236225
;
237226
%xs = shl nsw i6 %x, %z
@@ -242,9 +231,8 @@ define i6 @sub_shl_same_amount_partial_nsw1(i6 %x, i6 %y, i6 %z) {
242231

243232
define i6 @sub_shl_same_amount_partial_nsw2(i6 %x, i6 %y, i6 %z) {
244233
; CHECK-LABEL: @sub_shl_same_amount_partial_nsw2(
245-
; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
246-
; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
247-
; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i6 [[XS]], [[YS]]
234+
; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
235+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
248236
; CHECK-NEXT: ret i6 [[DIFF]]
249237
;
250238
%xs = shl i6 %x, %z
@@ -255,9 +243,8 @@ define i6 @sub_shl_same_amount_partial_nsw2(i6 %x, i6 %y, i6 %z) {
255243

256244
define i6 @sub_shl_same_amount_partial_nuw1(i6 %x, i6 %y, i6 %z) {
257245
; CHECK-LABEL: @sub_shl_same_amount_partial_nuw1(
258-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
259-
; CHECK-NEXT: [[YS:%.*]] = shl nuw i6 [[Y:%.*]], [[Z]]
260-
; CHECK-NEXT: [[DIFF:%.*]] = sub i6 [[XS]], [[YS]]
246+
; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
247+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
261248
; CHECK-NEXT: ret i6 [[DIFF]]
262249
;
263250
%xs = shl nuw i6 %x, %z
@@ -268,9 +255,8 @@ define i6 @sub_shl_same_amount_partial_nuw1(i6 %x, i6 %y, i6 %z) {
268255

269256
define i6 @sub_shl_same_amount_partial_nuw2(i6 %x, i6 %y, i6 %z) {
270257
; CHECK-LABEL: @sub_shl_same_amount_partial_nuw2(
271-
; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
272-
; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
273-
; CHECK-NEXT: [[DIFF:%.*]] = sub nuw i6 [[XS]], [[YS]]
258+
; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
259+
; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
274260
; CHECK-NEXT: ret i6 [[DIFF]]
275261
;
276262
%xs = shl nuw i6 %x, %z

0 commit comments

Comments
 (0)