Skip to content

Commit c662a9d

Browse files
authored
[InstCombine] recognize missed i128 split optimization (#129363)
This pr fixes #126056, recognising a split i128 extension optimization. Proof for working optimization: ```llvm define i128 @src(i32 noundef %x) { entry: %coerce.sroa.0.0.extract.trunc = sext i32 %x to i64 %0 = ashr i32 %x, 31 %coerce.sroa.2.0.extract.trunc = sext i32 %0 to i64 %x.sroa.2.0.insert.ext.i = zext i64 %coerce.sroa.2.0.extract.trunc to i128 %x.sroa.2.0.insert.shift.i = shl nuw i128 %x.sroa.2.0.insert.ext.i, 64 %x.sroa.0.0.insert.ext.i = zext i64 %coerce.sroa.0.0.extract.trunc to i128 %x.sroa.0.0.insert.insert.i = or disjoint i128 %x.sroa.2.0.insert.shift.i, %x.sroa.0.0.insert.ext.i ret i128 %x.sroa.0.0.insert.insert.i } define i128 @tgt(i32 noundef %x) { %x.sroa.0.0.insert.insert.i = sext i32 %x to i128 ret i128 %x.sroa.0.0.insert.insert.i } ```
1 parent 4f46b75 commit c662a9d

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3119,6 +3119,17 @@ static Value *matchOrConcat(Instruction &Or, InstCombiner::BuilderTy &Builder) {
31193119
match(UpperSrc, m_BitReverse(m_Value(UpperBRev))))
31203120
return ConcatIntrinsicCalls(Intrinsic::bitreverse, UpperBRev, LowerBRev);
31213121

3122+
// iX ext split: extending or(zext(x),shl(zext(y),bw/2) pattern
3123+
// to consume sext/ashr: or(zext(sext(x)),shl(zext(sext(ashr(x))),bw/2)
3124+
Value *X;
3125+
if (match(LowerSrc, m_SExt(m_Value(X))) &&
3126+
match(UpperSrc,
3127+
m_SExt(m_AShr(
3128+
m_Specific(X),
3129+
m_SpecificInt(X->getType()->getScalarSizeInBits() - 1))))) {
3130+
return Builder.CreateSExt(X, Ty);
3131+
}
3132+
31223133
return nullptr;
31233134
}
31243135

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
; PR129363
5+
6+
; ext split from i32 to i128
7+
define i128 @i128_ext_split(i32 noundef %x) {
8+
; CHECK-LABEL: define i128 @i128_ext_split(
9+
; CHECK-SAME: i32 noundef [[X:%.*]]) {
10+
; CHECK-NEXT: [[XX:%.*]] = sext i32 [[X]] to i128
11+
; CHECK-NEXT: ret i128 [[XX]]
12+
;
13+
%LowerSrc = sext i32 %x to i64
14+
%sign = ashr i32 %x, 31
15+
%UpperSrc = sext i32 %sign to i64
16+
%widen = zext i64 %UpperSrc to i128
17+
%hi = shl nuw i128 %widen, 64
18+
%lo = zext i64 %LowerSrc to i128
19+
%res = or disjoint i128 %hi, %lo
20+
ret i128 %res
21+
}
22+
23+
; ext split from i32 to i128
24+
define void @i128_ext_split_store(i32 %x, ptr %out) {
25+
; CHECK-LABEL: define void @i128_ext_split_store(
26+
; CHECK-SAME: i32 [[X:%.*]], ptr [[OUT:%.*]]) {
27+
; CHECK-NEXT: [[ENTRY:.*:]]
28+
; CHECK-NEXT: [[RES:%.*]] = sext i32 [[X]] to i128
29+
; CHECK-NEXT: store i128 [[RES]], ptr [[OUT]], align 16
30+
; CHECK-NEXT: ret void
31+
;
32+
entry:
33+
%LowerSrc = sext i32 %x to i64
34+
%lo = zext i64 %LowerSrc to i128
35+
36+
%sign = ashr i32 %x, 31
37+
%UpperSrc = sext i32 %sign to i64
38+
%widen = zext i64 %UpperSrc to i128
39+
%hi = shl nuw i128 %widen, 64
40+
41+
%res = or disjoint i128 %hi, %lo
42+
store i128 %res, ptr %out, align 16
43+
ret void
44+
}
45+
46+
; ext split from i16 to i64
47+
define void @i64_ext_split_store(i16 %x, ptr %out) {
48+
; CHECK-LABEL: define void @i64_ext_split_store(
49+
; CHECK-SAME: i16 [[X:%.*]], ptr [[OUT:%.*]]) {
50+
; CHECK-NEXT: [[ENTRY:.*:]]
51+
; CHECK-NEXT: [[RES:%.*]] = sext i16 [[X]] to i64
52+
; CHECK-NEXT: store i64 [[RES]], ptr [[OUT]], align 16
53+
; CHECK-NEXT: ret void
54+
;
55+
entry:
56+
%LowerSrc = sext i16 %x to i32
57+
%lo = zext i32 %LowerSrc to i64
58+
59+
%sign = ashr i16 %x, 15
60+
%UpperSrc = sext i16 %sign to i32
61+
%widen = zext i32 %UpperSrc to i64
62+
%hi = shl nuw i64 %widen, 32
63+
64+
%res = or disjoint i64 %hi, %lo
65+
store i64 %res, ptr %out, align 16
66+
ret void
67+
}
68+
69+
; ext split from i16 to i128
70+
define void @i128_ext_split_store_i16(i16 %x, ptr %out) {
71+
; CHECK-LABEL: define void @i128_ext_split_store_i16(
72+
; CHECK-SAME: i16 [[X:%.*]], ptr [[OUT:%.*]]) {
73+
; CHECK-NEXT: [[ENTRY:.*:]]
74+
; CHECK-NEXT: [[RES:%.*]] = sext i16 [[X]] to i128
75+
; CHECK-NEXT: store i128 [[RES]], ptr [[OUT]], align 16
76+
; CHECK-NEXT: ret void
77+
;
78+
entry:
79+
%LowerSrc = sext i16 %x to i64
80+
%lo = zext i64 %LowerSrc to i128
81+
82+
%sign = ashr i16 %x, 15
83+
%UpperSrc = sext i16 %sign to i64
84+
%widen = zext i64 %UpperSrc to i128
85+
%hi = shl nuw i128 %widen, 64
86+
87+
%res = or disjoint i128 %hi, %lo
88+
store i128 %res, ptr %out, align 16
89+
ret void
90+
}
91+
92+
; negative test - wrong constant value
93+
define i128 @i128_ext_split_neg1(i32 %x) {
94+
; CHECK-LABEL: define i128 @i128_ext_split_neg1(
95+
; CHECK-SAME: i32 [[X:%.*]]) {
96+
; CHECK-NEXT: [[ENTRY:.*:]]
97+
; CHECK-NEXT: [[LOWERSRC:%.*]] = sext i32 [[X]] to i64
98+
; CHECK-NEXT: [[LO:%.*]] = zext i64 [[LOWERSRC]] to i128
99+
; CHECK-NEXT: [[SIGN:%.*]] = ashr i32 [[X]], 31
100+
; CHECK-NEXT: [[UPPERSRC:%.*]] = sext i32 [[SIGN]] to i64
101+
; CHECK-NEXT: [[WIDEN:%.*]] = zext i64 [[UPPERSRC]] to i128
102+
; CHECK-NEXT: [[HI:%.*]] = shl nuw i128 [[WIDEN]], 65
103+
; CHECK-NEXT: [[RES:%.*]] = or disjoint i128 [[HI]], [[LO]]
104+
; CHECK-NEXT: ret i128 [[RES]]
105+
;
106+
entry:
107+
%LowerSrc = sext i32 %x to i64
108+
%lo = zext i64 %LowerSrc to i128
109+
110+
%sign = ashr i32 %x, 31
111+
%UpperSrc = sext i32 %sign to i64
112+
%widen = zext i64 %UpperSrc to i128
113+
%hi = shl nuw i128 %widen, 65
114+
115+
%res = or disjoint i128 %hi, %lo
116+
ret i128 %res
117+
}
118+
119+
; negative test - wrong shift value
120+
define i128 @i128_ext_split_neg2(i32 %x) {
121+
; CHECK-LABEL: define i128 @i128_ext_split_neg2(
122+
; CHECK-SAME: i32 [[X:%.*]]) {
123+
; CHECK-NEXT: [[ENTRY:.*:]]
124+
; CHECK-NEXT: [[LOWERSRC:%.*]] = sext i32 [[X]] to i64
125+
; CHECK-NEXT: [[LO:%.*]] = zext i64 [[LOWERSRC]] to i128
126+
; CHECK-NEXT: [[SIGN:%.*]] = ashr i32 [[X]], 3
127+
; CHECK-NEXT: [[UPPERSRC:%.*]] = sext i32 [[SIGN]] to i64
128+
; CHECK-NEXT: [[WIDEN:%.*]] = zext i64 [[UPPERSRC]] to i128
129+
; CHECK-NEXT: [[HI:%.*]] = shl nuw i128 [[WIDEN]], 64
130+
; CHECK-NEXT: [[RES:%.*]] = or disjoint i128 [[HI]], [[LO]]
131+
; CHECK-NEXT: ret i128 [[RES]]
132+
;
133+
entry:
134+
%LowerSrc = sext i32 %x to i64
135+
%lo = zext i64 %LowerSrc to i128
136+
137+
%sign = ashr i32 %x, 3
138+
%UpperSrc = sext i32 %sign to i64
139+
%widen = zext i64 %UpperSrc to i128
140+
%hi = shl nuw i128 %widen, 64
141+
142+
%res = or disjoint i128 %hi, %lo
143+
ret i128 %res
144+
}
145+
146+
; negative test - wrong ext instruction
147+
define i128 @i128_ext_split_neg3(i32 %x) {
148+
; CHECK-LABEL: define i128 @i128_ext_split_neg3(
149+
; CHECK-SAME: i32 [[X:%.*]]) {
150+
; CHECK-NEXT: [[ENTRY:.*:]]
151+
; CHECK-NEXT: [[LO:%.*]] = zext i32 [[X]] to i128
152+
; CHECK-NEXT: [[SIGN:%.*]] = ashr i32 [[X]], 31
153+
; CHECK-NEXT: [[UPPERSRC:%.*]] = sext i32 [[SIGN]] to i64
154+
; CHECK-NEXT: [[WIDEN:%.*]] = zext i64 [[UPPERSRC]] to i128
155+
; CHECK-NEXT: [[HI:%.*]] = shl nuw i128 [[WIDEN]], 64
156+
; CHECK-NEXT: [[RES:%.*]] = or disjoint i128 [[HI]], [[LO]]
157+
; CHECK-NEXT: ret i128 [[RES]]
158+
;
159+
entry:
160+
%LowerSrc = zext i32 %x to i64
161+
%lo = zext i64 %LowerSrc to i128
162+
163+
%sign = ashr i32 %x, 31
164+
%UpperSrc = sext i32 %sign to i64
165+
%widen = zext i64 %UpperSrc to i128
166+
%hi = shl nuw i128 %widen, 64
167+
168+
%res = or disjoint i128 %hi, %lo
169+
ret i128 %res
170+
}
171+
172+
; negative test - wrong shift
173+
define i128 @i128_ext_split_neg4(i32 %x) {
174+
; CHECK-LABEL: define i128 @i128_ext_split_neg4(
175+
; CHECK-SAME: i32 [[X:%.*]]) {
176+
; CHECK-NEXT: [[ENTRY:.*:]]
177+
; CHECK-NEXT: [[LOWERSRC:%.*]] = sext i32 [[X]] to i64
178+
; CHECK-NEXT: [[LO:%.*]] = zext i64 [[LOWERSRC]] to i128
179+
; CHECK-NEXT: [[SIGN:%.*]] = lshr i32 [[X]], 31
180+
; CHECK-NEXT: [[WIDEN:%.*]] = zext nneg i32 [[SIGN]] to i128
181+
; CHECK-NEXT: [[HI:%.*]] = shl nuw nsw i128 [[WIDEN]], 64
182+
; CHECK-NEXT: [[RES:%.*]] = or disjoint i128 [[HI]], [[LO]]
183+
; CHECK-NEXT: ret i128 [[RES]]
184+
;
185+
entry:
186+
%LowerSrc = sext i32 %x to i64
187+
%lo = zext i64 %LowerSrc to i128
188+
189+
%sign = lshr i32 %x, 31
190+
%UpperSrc = sext i32 %sign to i64
191+
%widen = zext i64 %UpperSrc to i128
192+
%hi = shl nuw i128 %widen, 64
193+
194+
%res = or disjoint i128 %hi, %lo
195+
ret i128 %res
196+
}

0 commit comments

Comments
 (0)