1 //===- llvm/unittest/CodeGen/GlobalISel/LegalizerInfoTest.cpp -------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
10 #include "llvm/CodeGen/TargetOpcodes.h"
11 #include "GISelMITest.h"
12 #include "gtest/gtest.h"
13
14 using namespace llvm;
15 using namespace LegalizeActions;
16
17 // Define a couple of pretty printers to help debugging when things go wrong.
18 namespace llvm {
19 std::ostream &
operator <<(std::ostream & OS,const LegalizeAction Act)20 operator<<(std::ostream &OS, const LegalizeAction Act) {
21 switch (Act) {
22 case Lower: OS << "Lower"; break;
23 case Legal: OS << "Legal"; break;
24 case NarrowScalar: OS << "NarrowScalar"; break;
25 case WidenScalar: OS << "WidenScalar"; break;
26 case FewerElements: OS << "FewerElements"; break;
27 case MoreElements: OS << "MoreElements"; break;
28 case Libcall: OS << "Libcall"; break;
29 case Custom: OS << "Custom"; break;
30 case Bitcast: OS << "Bitcast"; break;
31 case Unsupported: OS << "Unsupported"; break;
32 case NotFound: OS << "NotFound"; break;
33 case UseLegacyRules: OS << "UseLegacyRules"; break;
34 }
35 return OS;
36 }
37
operator <<(std::ostream & OS,const llvm::LegalizeActionStep Ty)38 std::ostream &operator<<(std::ostream &OS, const llvm::LegalizeActionStep Ty) {
39 OS << "LegalizeActionStep(" << Ty.Action << ", " << Ty.TypeIdx << ", "
40 << Ty.NewType << ')';
41 return OS;
42 }
43 }
44
45 namespace {
46
47
TEST(LegalizerInfoTest,ScalarRISC)48 TEST(LegalizerInfoTest, ScalarRISC) {
49 using namespace TargetOpcode;
50 LegalizerInfo L;
51 // Typical RISCy set of operations based on AArch64.
52 for (unsigned Op : {G_ADD, G_SUB}) {
53 for (unsigned Size : {32, 64})
54 L.setAction({Op, 0, LLT::scalar(Size)}, Legal);
55 L.setLegalizeScalarToDifferentSizeStrategy(
56 Op, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest);
57 }
58
59 L.computeTables();
60
61 for (unsigned opcode : {G_ADD, G_SUB}) {
62 // Check we infer the correct types and actually do what we're told.
63 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(8)}}),
64 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
65 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(16)}}),
66 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
67 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(32)}}),
68 LegalizeActionStep(Legal, 0, LLT{}));
69 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(64)}}),
70 LegalizeActionStep(Legal, 0, LLT{}));
71
72 // Make sure the default for over-sized types applies.
73 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(128)}}),
74 LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64)));
75 // Make sure we also handle unusual sizes
76 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(1)}}),
77 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
78 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(31)}}),
79 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
80 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(33)}}),
81 LegalizeActionStep(WidenScalar, 0, LLT::scalar(64)));
82 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(63)}}),
83 LegalizeActionStep(WidenScalar, 0, LLT::scalar(64)));
84 EXPECT_EQ(L.getAction({opcode, {LLT::scalar(65)}}),
85 LegalizeActionStep(NarrowScalar, 0, LLT::scalar(64)));
86 }
87 }
88
TEST(LegalizerInfoTest,VectorRISC)89 TEST(LegalizerInfoTest, VectorRISC) {
90 using namespace TargetOpcode;
91 LegalizerInfo L;
92 // Typical RISCy set of operations based on ARM.
93 L.setAction({G_ADD, LLT::vector(8, 8)}, Legal);
94 L.setAction({G_ADD, LLT::vector(16, 8)}, Legal);
95 L.setAction({G_ADD, LLT::vector(4, 16)}, Legal);
96 L.setAction({G_ADD, LLT::vector(8, 16)}, Legal);
97 L.setAction({G_ADD, LLT::vector(2, 32)}, Legal);
98 L.setAction({G_ADD, LLT::vector(4, 32)}, Legal);
99
100 L.setLegalizeVectorElementToDifferentSizeStrategy(
101 G_ADD, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
102
103 L.setAction({G_ADD, 0, LLT::scalar(32)}, Legal);
104
105 L.computeTables();
106
107 // Check we infer the correct types and actually do what we're told for some
108 // simple cases.
109 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(8, 8)}}),
110 LegalizeActionStep(Legal, 0, LLT{}));
111 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(8, 7)}}),
112 LegalizeActionStep(WidenScalar, 0, LLT::vector(8, 8)));
113 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(2, 8)}}),
114 LegalizeActionStep(MoreElements, 0, LLT::vector(8, 8)));
115 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(8, 32)}}),
116 LegalizeActionStep(FewerElements, 0, LLT::vector(4, 32)));
117 // Check a few non-power-of-2 sizes:
118 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(3, 3)}}),
119 LegalizeActionStep(WidenScalar, 0, LLT::vector(3, 8)));
120 EXPECT_EQ(L.getAction({G_ADD, {LLT::vector(3, 8)}}),
121 LegalizeActionStep(MoreElements, 0, LLT::vector(8, 8)));
122 }
123
TEST(LegalizerInfoTest,MultipleTypes)124 TEST(LegalizerInfoTest, MultipleTypes) {
125 using namespace TargetOpcode;
126 LegalizerInfo L;
127 LLT p0 = LLT::pointer(0, 64);
128 LLT s64 = LLT::scalar(64);
129
130 // Typical RISCy set of operations based on AArch64.
131 L.setAction({G_PTRTOINT, 0, s64}, Legal);
132 L.setAction({G_PTRTOINT, 1, p0}, Legal);
133
134 L.setLegalizeScalarToDifferentSizeStrategy(
135 G_PTRTOINT, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest);
136
137 L.computeTables();
138
139 // Check we infer the correct types and actually do what we're told.
140 EXPECT_EQ(L.getAction({G_PTRTOINT, {s64, p0}}),
141 LegalizeActionStep(Legal, 0, LLT{}));
142
143 // Make sure we also handle unusual sizes
144 EXPECT_EQ(
145 L.getAction({G_PTRTOINT, {LLT::scalar(65), s64}}),
146 LegalizeActionStep(NarrowScalar, 0, s64));
147 EXPECT_EQ(
148 L.getAction({G_PTRTOINT, {s64, LLT::pointer(0, 32)}}),
149 LegalizeActionStep(Unsupported, 1, LLT::pointer(0, 32)));
150 }
151
TEST(LegalizerInfoTest,MultipleSteps)152 TEST(LegalizerInfoTest, MultipleSteps) {
153 using namespace TargetOpcode;
154 LegalizerInfo L;
155 LLT s32 = LLT::scalar(32);
156 LLT s64 = LLT::scalar(64);
157
158 L.setLegalizeScalarToDifferentSizeStrategy(
159 G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
160 L.setAction({G_UREM, 0, s32}, Lower);
161 L.setAction({G_UREM, 0, s64}, Lower);
162
163 L.computeTables();
164
165 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(16)}}),
166 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
167 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(32)}}),
168 LegalizeActionStep(Lower, 0, LLT::scalar(32)));
169 }
170
TEST(LegalizerInfoTest,SizeChangeStrategy)171 TEST(LegalizerInfoTest, SizeChangeStrategy) {
172 using namespace TargetOpcode;
173 LegalizerInfo L;
174 for (unsigned Size : {1, 8, 16, 32})
175 L.setAction({G_UREM, 0, LLT::scalar(Size)}, Legal);
176
177 L.setLegalizeScalarToDifferentSizeStrategy(
178 G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise);
179 L.computeTables();
180
181 // Check we infer the correct types and actually do what we're told.
182 for (unsigned Size : {1, 8, 16, 32}) {
183 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(Size)}}),
184 LegalizeActionStep(Legal, 0, LLT{}));
185 }
186 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(2)}}),
187 LegalizeActionStep(WidenScalar, 0, LLT::scalar(8)));
188 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(7)}}),
189 LegalizeActionStep(WidenScalar, 0, LLT::scalar(8)));
190 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(9)}}),
191 LegalizeActionStep(WidenScalar, 0, LLT::scalar(16)));
192 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(17)}}),
193 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
194 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(31)}}),
195 LegalizeActionStep(WidenScalar, 0, LLT::scalar(32)));
196 EXPECT_EQ(L.getAction({G_UREM, {LLT::scalar(33)}}),
197 LegalizeActionStep(Unsupported, 0, LLT::scalar(33)));
198 }
199 }
200
201 #define EXPECT_ACTION(Action, Index, Type, Query) \
202 do { \
203 auto A = LI.getAction(Query); \
204 EXPECT_EQ(LegalizeActionStep(Action, Index, Type), A) << A; \
205 } while (0)
206
TEST(LegalizerInfoTest,RuleSets)207 TEST(LegalizerInfoTest, RuleSets) {
208 using namespace TargetOpcode;
209
210 const LLT s5 = LLT::scalar(5);
211 const LLT s8 = LLT::scalar(8);
212 const LLT s16 = LLT::scalar(16);
213 const LLT s32 = LLT::scalar(32);
214 const LLT s33 = LLT::scalar(33);
215 const LLT s64 = LLT::scalar(64);
216
217 const LLT v2s5 = LLT::vector(2, 5);
218 const LLT v2s8 = LLT::vector(2, 8);
219 const LLT v2s16 = LLT::vector(2, 16);
220 const LLT v2s32 = LLT::vector(2, 32);
221 const LLT v3s32 = LLT::vector(3, 32);
222 const LLT v4s32 = LLT::vector(4, 32);
223 const LLT v2s33 = LLT::vector(2, 33);
224 const LLT v2s64 = LLT::vector(2, 64);
225
226 const LLT p0 = LLT::pointer(0, 32);
227 const LLT v3p0 = LLT::vector(3, p0);
228 const LLT v4p0 = LLT::vector(4, p0);
229
230 {
231 LegalizerInfo LI;
232
233 LI.getActionDefinitionsBuilder(G_IMPLICIT_DEF)
234 .legalFor({v4s32, v4p0})
235 .moreElementsToNextPow2(0);
236 LI.computeTables();
237
238 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {s32}));
239 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_IMPLICIT_DEF, {v2s32}));
240 EXPECT_ACTION(MoreElements, 0, v4p0, LegalityQuery(G_IMPLICIT_DEF, {v3p0}));
241 EXPECT_ACTION(MoreElements, 0, v4s32, LegalityQuery(G_IMPLICIT_DEF, {v3s32}));
242 }
243
244 // Test minScalarOrElt
245 {
246 LegalizerInfo LI;
247 LI.getActionDefinitionsBuilder(G_OR)
248 .legalFor({s32})
249 .minScalarOrElt(0, s32);
250 LI.computeTables();
251
252 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16}));
253 EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_OR, {v2s16}));
254 }
255
256 // Test maxScalarOrELt
257 {
258 LegalizerInfo LI;
259 LI.getActionDefinitionsBuilder(G_AND)
260 .legalFor({s16})
261 .maxScalarOrElt(0, s16);
262 LI.computeTables();
263
264 EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32}));
265 EXPECT_ACTION(NarrowScalar, 0, v2s16, LegalityQuery(G_AND, {v2s32}));
266 }
267
268 // Test clampScalarOrElt
269 {
270 LegalizerInfo LI;
271 LI.getActionDefinitionsBuilder(G_XOR)
272 .legalFor({s16})
273 .clampScalarOrElt(0, s16, s32);
274 LI.computeTables();
275
276 EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64}));
277 EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8}));
278
279 // Make sure the number of elements is preserved.
280 EXPECT_ACTION(NarrowScalar, 0, v2s32, LegalityQuery(G_XOR, {v2s64}));
281 EXPECT_ACTION(WidenScalar, 0, v2s16, LegalityQuery(G_XOR, {v2s8}));
282 }
283
284 // Test minScalar
285 {
286 LegalizerInfo LI;
287 LI.getActionDefinitionsBuilder(G_OR)
288 .legalFor({s32})
289 .minScalar(0, s32);
290 LI.computeTables();
291
292 // Only handle scalars, ignore vectors.
293 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_OR, {s16}));
294 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_OR, {v2s16}));
295 }
296
297 // Test maxScalar
298 {
299 LegalizerInfo LI;
300 LI.getActionDefinitionsBuilder(G_AND)
301 .legalFor({s16})
302 .maxScalar(0, s16);
303 LI.computeTables();
304
305 // Only handle scalars, ignore vectors.
306 EXPECT_ACTION(NarrowScalar, 0, s16, LegalityQuery(G_AND, {s32}));
307 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s32}));
308 }
309
310 // Test clampScalar
311 {
312 LegalizerInfo LI;
313
314 LI.getActionDefinitionsBuilder(G_XOR)
315 .legalFor({s16})
316 .clampScalar(0, s16, s32);
317 LI.computeTables();
318
319 EXPECT_ACTION(NarrowScalar, 0, s32, LegalityQuery(G_XOR, {s64}));
320 EXPECT_ACTION(WidenScalar, 0, s16, LegalityQuery(G_XOR, {s8}));
321
322 // Only handle scalars, ignore vectors.
323 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s64}));
324 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_XOR, {v2s8}));
325 }
326
327 // Test widenScalarOrEltToNextPow2
328 {
329 LegalizerInfo LI;
330
331 LI.getActionDefinitionsBuilder(G_AND)
332 .legalFor({s32})
333 .widenScalarOrEltToNextPow2(0, 32);
334 LI.computeTables();
335
336 // Handle scalars and vectors
337 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5}));
338 EXPECT_ACTION(WidenScalar, 0, v2s32, LegalityQuery(G_AND, {v2s5}));
339 EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33}));
340 EXPECT_ACTION(WidenScalar, 0, v2s64, LegalityQuery(G_AND, {v2s33}));
341 }
342
343 // Test widenScalarToNextPow2
344 {
345 LegalizerInfo LI;
346
347 LI.getActionDefinitionsBuilder(G_AND)
348 .legalFor({s32})
349 .widenScalarToNextPow2(0, 32);
350 LI.computeTables();
351
352 EXPECT_ACTION(WidenScalar, 0, s32, LegalityQuery(G_AND, {s5}));
353 EXPECT_ACTION(WidenScalar, 0, s64, LegalityQuery(G_AND, {s33}));
354
355 // Do nothing for vectors.
356 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s5}));
357 EXPECT_ACTION(Unsupported, 0, LLT(), LegalityQuery(G_AND, {v2s33}));
358 }
359 }
360
TEST(LegalizerInfoTest,MMOAlignment)361 TEST(LegalizerInfoTest, MMOAlignment) {
362 using namespace TargetOpcode;
363
364 const LLT s32 = LLT::scalar(32);
365 const LLT p0 = LLT::pointer(0, 64);
366
367 {
368 LegalizerInfo LI;
369 LI.getActionDefinitionsBuilder(G_LOAD)
370 .legalForTypesWithMemDesc({{s32, p0, 32, 32}});
371
372 LI.computeTables();
373
374 EXPECT_ACTION(Legal, 0, LLT(),
375 LegalityQuery(G_LOAD, {s32, p0},
376 LegalityQuery::MemDesc{
377 32, 32, AtomicOrdering::NotAtomic}));
378 EXPECT_ACTION(Unsupported, 0, LLT(),
379 LegalityQuery(G_LOAD, {s32, p0},
380 LegalityQuery::MemDesc{
381 32, 16, AtomicOrdering::NotAtomic }));
382 EXPECT_ACTION(Unsupported, 0, LLT(),
383 LegalityQuery(G_LOAD, {s32, p0},
384 LegalityQuery::MemDesc{
385 32, 8, AtomicOrdering::NotAtomic}));
386 }
387
388 // Test that the maximum supported alignment value isn't truncated
389 {
390 // Maximum IR defined alignment in bytes.
391 const uint64_t MaxAlignment = UINT64_C(1) << 29;
392 const uint64_t MaxAlignInBits = 8 * MaxAlignment;
393 LegalizerInfo LI;
394 LI.getActionDefinitionsBuilder(G_LOAD)
395 .legalForTypesWithMemDesc({{s32, p0, 32, MaxAlignInBits}});
396
397 LI.computeTables();
398
399 EXPECT_ACTION(Legal, 0, LLT(),
400 LegalityQuery(G_LOAD, {s32, p0},
401 LegalityQuery::MemDesc{32,
402 MaxAlignInBits, AtomicOrdering::NotAtomic}));
403 EXPECT_ACTION(Unsupported, 0, LLT(),
404 LegalityQuery(G_LOAD, {s32, p0},
405 LegalityQuery::MemDesc{
406 32, 8, AtomicOrdering::NotAtomic }));
407 }
408 }
409
410 // This code sequence doesn't do anything, but it covers a previously uncovered
411 // codepath that used to crash in MSVC x86_32 debug mode.
TEST(LegalizerInfoTest,MSVCDebugMiscompile)412 TEST(LegalizerInfoTest, MSVCDebugMiscompile) {
413 const LLT S1 = LLT::scalar(1);
414 const LLT P0 = LLT::pointer(0, 32);
415 LegalizerInfo LI;
416 auto Builder = LI.getActionDefinitionsBuilder(TargetOpcode::G_PTRTOINT);
417 (void)Builder.legalForCartesianProduct({S1}, {P0});
418 }
419