LLVM 24.0.0git
AArch64LegalizerInfo.cpp
Go to the documentation of this file.
1//===- AArch64LegalizerInfo.cpp ----------------------------------*- C++ -*-==//
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/// \file
9/// This file implements the targeting of the Machinelegalizer class for
10/// AArch64.
11/// \todo This should be generated by TableGen.
12//===----------------------------------------------------------------------===//
13
15#include "AArch64Subtarget.h"
16#include "llvm/ADT/STLExtras.h"
28#include "llvm/IR/Intrinsics.h"
29#include "llvm/IR/IntrinsicsAArch64.h"
30#include "llvm/IR/Type.h"
32#include <initializer_list>
33
34#define DEBUG_TYPE "aarch64-legalinfo"
35
36using namespace llvm;
37using namespace LegalizeActions;
38using namespace LegalizeMutations;
39using namespace LegalityPredicates;
40using namespace MIPatternMatch;
41
43 : ST(&ST) {
44 using namespace TargetOpcode;
45 const LLT p0 = LLT::pointer(0, 64);
46 const LLT s8 = LLT::scalar(8);
47 const LLT s16 = LLT::scalar(16);
48 const LLT s32 = LLT::scalar(32);
49 const LLT s64 = LLT::scalar(64);
50 const LLT s128 = LLT::scalar(128);
51 const LLT v16s8 = LLT::fixed_vector(16, 8);
52 const LLT v8s8 = LLT::fixed_vector(8, 8);
53 const LLT v4s8 = LLT::fixed_vector(4, 8);
54 const LLT v2s8 = LLT::fixed_vector(2, 8);
55 const LLT v8s16 = LLT::fixed_vector(8, 16);
56 const LLT v4s16 = LLT::fixed_vector(4, 16);
57 const LLT v2s16 = LLT::fixed_vector(2, 16);
58 const LLT v2s32 = LLT::fixed_vector(2, 32);
59 const LLT v4s32 = LLT::fixed_vector(4, 32);
60 const LLT v2s64 = LLT::fixed_vector(2, 64);
61 const LLT v2p0 = LLT::fixed_vector(2, p0);
62
63 const LLT nxv16s8 = LLT::scalable_vector(16, s8);
64 const LLT nxv8s16 = LLT::scalable_vector(8, s16);
65 const LLT nxv4s32 = LLT::scalable_vector(4, s32);
66 const LLT nxv2s64 = LLT::scalable_vector(2, s64);
67
68 const LLT bf16 = LLT::bfloat16();
69 const LLT v4bf16 = LLT::fixed_vector(4, bf16);
70 const LLT v8bf16 = LLT::fixed_vector(8, bf16);
71
72 const LLT f16 = LLT::float16();
73 const LLT v4f16 = LLT::fixed_vector(4, f16);
74 const LLT v8f16 = LLT::fixed_vector(8, f16);
75
76 const LLT f32 = LLT::float32();
77 const LLT v2f32 = LLT::fixed_vector(2, f32);
78 const LLT v4f32 = LLT::fixed_vector(4, f32);
79
80 const LLT f64 = LLT::float64();
81 const LLT v2f64 = LLT::fixed_vector(2, f64);
82
83 const LLT f128 = LLT::float128();
84
85 const LLT i8 = LLT::integer(8);
86 const LLT v8i8 = LLT::fixed_vector(8, i8);
87 const LLT v16i8 = LLT::fixed_vector(16, i8);
88
89 const LLT i16 = LLT::integer(16);
90 const LLT v8i16 = LLT::fixed_vector(8, i16);
91 const LLT v4i16 = LLT::fixed_vector(4, i16);
92
93 const LLT i32 = LLT::integer(32);
94 const LLT v2i32 = LLT::fixed_vector(2, i32);
95 const LLT v4i32 = LLT::fixed_vector(4, i32);
96
97 const LLT i64 = LLT::integer(64);
98 const LLT v2i64 = LLT::fixed_vector(2, i64);
99
100 const LLT i128 = LLT::integer(128);
101
102 const LLT nxv16i8 = LLT::scalable_vector(16, i8);
103 const LLT nxv8i16 = LLT::scalable_vector(8, i16);
104 const LLT nxv4i32 = LLT::scalable_vector(4, i32);
105 const LLT nxv2i64 = LLT::scalable_vector(2, i64);
106
107 std::initializer_list<LLT> PackedVectorAllTypeList = {/* Begin 128bit types */
108 v16s8, v8s16, v4s32,
109 v2s64, v2p0,
110 /* End 128bit types */
111 /* Begin 64bit types */
112 v8s8, v4s16, v2s32};
113 std::initializer_list<LLT> ScalarAndPtrTypesList = {s8, s16, s32, s64, p0};
114 SmallVector<LLT, 8> PackedVectorAllTypesVec(PackedVectorAllTypeList);
115 SmallVector<LLT, 8> ScalarAndPtrTypesVec(ScalarAndPtrTypesList);
116
117 const TargetMachine &TM = ST.getTargetLowering()->getTargetMachine();
118
119 // FIXME: support subtargets which have neon/fp-armv8 disabled.
120 if (!ST.hasNEON() || !ST.hasFPARMv8())
121 return;
122
123 // Some instructions only support s16 if the subtarget has full 16-bit FP
124 // support.
125 const bool HasFP16 = ST.hasFullFP16();
126 const bool HasCSSC = ST.hasCSSC();
127 const bool HasRCPC3 = ST.hasRCPC3();
128 const bool HasSVE = ST.hasSVE();
129
131 {G_IMPLICIT_DEF, G_FREEZE, G_CONSTANT_FOLD_BARRIER})
132 .legalFor({p0, s8, s16, s32, s64, s128})
133 .legalFor({v2s8, v4s8, v8s8, v16s8, v2s16, v4s16, v8s16, v2s32, v4s32,
134 v2s64, v2p0})
135 .widenScalarToNextPow2(0)
136 .clampScalar(0, s8, s64)
139 .clampNumElements(0, v8s8, v16s8)
140 .clampNumElements(0, v4s16, v8s16)
141 .clampNumElements(0, v2s32, v4s32)
142 .clampMaxNumElements(0, s64, 2)
143 .clampMaxNumElements(0, p0, 2)
145
147 .legalFor({p0, s16, s32, s64})
148 .legalFor(PackedVectorAllTypeList)
152 .clampScalar(0, s16, s64)
153 .clampNumElements(0, v8s8, v16s8)
154 .clampNumElements(0, v4s16, v8s16)
155 .clampNumElements(0, v2s32, v4s32)
156 .clampMaxNumElements(0, s64, 2)
157 .clampMaxNumElements(0, p0, 2)
159
161 .legalIf(all(typeInSet(0, {s32, s64, p0}), typeInSet(1, {s8, s16, s32}),
162 smallerThan(1, 0)))
163 .widenScalarToNextPow2(0)
164 .clampScalar(0, s32, s64)
166 .minScalar(1, s8)
167 .maxScalarIf(typeInSet(0, {s32}), 1, s16)
168 .maxScalarIf(typeInSet(0, {s64, p0}), 1, s32);
169
171 .legalIf(all(typeInSet(0, {s16, s32, s64, p0}),
172 typeInSet(1, {s32, s64, s128, p0}), smallerThan(0, 1)))
173 .widenScalarToNextPow2(1)
174 .clampScalar(1, s32, s128)
176 .minScalar(0, s16)
177 .maxScalarIf(typeInSet(1, {s32}), 0, s16)
178 .maxScalarIf(typeInSet(1, {s64, p0}), 0, s32)
179 .maxScalarIf(typeInSet(1, {s128}), 0, s64);
180
181 getActionDefinitionsBuilder({G_ADD, G_SUB, G_AND, G_OR, G_XOR})
182 .legalFor({i32, i64, v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
183 .legalFor(HasSVE, {nxv16i8, nxv8i16, nxv4i32, nxv2i64})
184 .widenScalarToNextPow2(0)
185 .clampScalar(0, s32, s64)
186 .clampMaxNumElements(0, s8, 16)
187 .clampMaxNumElements(0, s16, 8)
188 .clampNumElements(0, v2s32, v4s32)
189 .clampNumElements(0, v2s64, v2s64)
191 [=](const LegalityQuery &Query) {
192 return Query.Types[0].getNumElements() <= 2;
193 },
194 0, s32)
195 .minScalarOrEltIf(
196 [=](const LegalityQuery &Query) {
197 return Query.Types[0].getNumElements() <= 4;
198 },
199 0, s16)
200 .minScalarOrEltIf(
201 [=](const LegalityQuery &Query) {
202 return Query.Types[0].getNumElements() <= 16;
203 },
204 0, s8)
205 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
207
209 .legalFor({i32, i64, v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
210 .widenScalarToNextPow2(0)
211 .clampScalar(0, s32, s64)
212 .clampMaxNumElements(0, s8, 16)
213 .clampMaxNumElements(0, s16, 8)
214 .clampNumElements(0, v2s32, v4s32)
215 .clampNumElements(0, v2s64, v2s64)
217 [=](const LegalityQuery &Query) {
218 return Query.Types[0].getNumElements() <= 2;
219 },
220 0, s32)
221 .minScalarOrEltIf(
222 [=](const LegalityQuery &Query) {
223 return Query.Types[0].getNumElements() <= 4;
224 },
225 0, s16)
226 .minScalarOrEltIf(
227 [=](const LegalityQuery &Query) {
228 return Query.Types[0].getNumElements() <= 16;
229 },
230 0, s8)
231 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
233
234 getActionDefinitionsBuilder({G_SHL, G_ASHR, G_LSHR})
235 .customIf([=](const LegalityQuery &Query) {
236 const auto &SrcTy = Query.Types[0];
237 const auto &AmtTy = Query.Types[1];
238 return !SrcTy.isVector() && SrcTy.getSizeInBits() == 32 &&
239 AmtTy.getSizeInBits() == 32;
240 })
241 .legalFor({
242 {i32, i32},
243 {i32, i64},
244 {i64, i64},
245 {v8i8, v8i8},
246 {v16i8, v16i8},
247 {v4i16, v4i16},
248 {v8i16, v8i16},
249 {v2i32, v2i32},
250 {v4i32, v4i32},
251 {v2i64, v2i64},
252 })
253 .widenScalarToNextPow2(1)
255 .clampScalar(1, s32, s64)
256 .clampScalar(0, s32, s64)
257 .clampNumElements(0, v8s8, v16s8)
258 .clampNumElements(0, v4s16, v8s16)
259 .clampNumElements(0, v2s32, v4s32)
260 .clampNumElements(0, v2s64, v2s64)
262 .minScalarSameAs(1, 0)
266
268 .legalFor({{p0, i64}, {v2p0, v2i64}})
269 .clampScalarOrElt(1, s64, s64)
270 .clampNumElements(0, v2p0, v2p0);
271
272 getActionDefinitionsBuilder(G_PTRMASK).legalFor({{p0, s64}});
273
274 getActionDefinitionsBuilder({G_SDIV, G_UDIV})
275 .legalFor({i32, i64})
276 .libcallFor({i128})
277 .clampScalar(0, s32, s64)
279 .scalarize(0);
280
281 getActionDefinitionsBuilder({G_SREM, G_UREM, G_SDIVREM, G_UDIVREM})
282 .lowerFor({i8, i16, i32, i64, v2i32, v4i32, v2i64})
283 .libcallFor({i128})
285 .minScalarOrElt(0, s32)
286 .clampNumElements(0, v2s32, v4s32)
287 .clampNumElements(0, v2s64, v2s64)
288 .scalarize(0);
289
290 getActionDefinitionsBuilder({G_SMULO, G_UMULO})
291 .widenScalarToNextPow2(0, /*Min = */ 32)
292 .clampScalar(0, s32, s64)
293 .lower();
294
295 getActionDefinitionsBuilder({G_SMULH, G_UMULH})
296 .legalFor({i64, v16i8, v8i16, v4i32})
297 .lower();
298
300 {G_SMULFIX, G_UMULFIX, G_SMULFIXSAT, G_UMULFIXSAT})
301 .lower();
302
303 getActionDefinitionsBuilder({G_SMIN, G_SMAX, G_UMIN, G_UMAX})
304 .legalFor({v8i8, v16i8, v4i16, v8i16, v2i32, v4i32})
305 .legalFor(HasCSSC, {i32, i64})
306 .minScalar(HasCSSC, 0, s32)
307 .clampNumElements(0, v8s8, v16s8)
308 .clampNumElements(0, v4s16, v8s16)
309 .clampNumElements(0, v2s32, v4s32)
310 .lower();
311
312 // FIXME: Legal vector types are only legal with NEON.
314 .legalFor(HasCSSC, {i32, i64})
315 .legalFor({v16i8, v8i16, v4i32, v2i64, v2p0, v8i8, v4i16, v2i32})
316 .customIf([=](const LegalityQuery &Q) {
317 // TODO: Fix suboptimal codegen for 128+ bit types.
318 LLT SrcTy = Q.Types[0];
319 return SrcTy.isScalar() && SrcTy.getSizeInBits() < 128;
320 })
321 .widenScalarIf(
322 [=](const LegalityQuery &Query) { return Query.Types[0] == v4s8; },
323 [=](const LegalityQuery &Query) { return std::make_pair(0, v4i16); })
324 .widenScalarIf(
325 [=](const LegalityQuery &Query) { return Query.Types[0] == v2s16; },
326 [=](const LegalityQuery &Query) { return std::make_pair(0, v2i32); })
327 .clampNumElements(0, v8s8, v16s8)
328 .clampNumElements(0, v4s16, v8s16)
329 .clampNumElements(0, v2s32, v4s32)
330 .clampNumElements(0, v2s64, v2s64)
332 .lower();
333
335 {G_ABDS, G_ABDU, G_UAVGFLOOR, G_UAVGCEIL, G_SAVGFLOOR, G_SAVGCEIL})
336 .legalFor({v8i8, v16i8, v4i16, v8i16, v2i32, v4i32})
337 .lower();
338
340 {G_SADDE, G_SSUBE, G_UADDE, G_USUBE, G_SADDO, G_SSUBO, G_UADDO, G_USUBO})
341 .legalFor({{i32, i32}, {i64, i32}})
342 .clampScalar(0, s32, s64)
343 .clampScalar(1, s32, s64)
345
346 getActionDefinitionsBuilder({G_FSHL, G_FSHR})
347 .customFor({{i32, i32}, {i32, i64}, {i64, i64}})
348 .lower();
349
351 .legalFor({{i32, i64}, {i64, i64}})
352 .customIf([=](const LegalityQuery &Q) {
353 return Q.Types[0].isScalar() && Q.Types[1].getScalarSizeInBits() < 64;
354 })
355 .lower();
357
358 getActionDefinitionsBuilder({G_SBFX, G_UBFX})
359 .customFor({{s32, s32}, {s64, s64}});
360
361 auto always = [=](const LegalityQuery &Q) { return true; };
363 .legalFor(HasCSSC, {{i32, i32}, {i64, i64}})
364 .legalFor({{v8i8, v8i8}, {v16i8, v16i8}})
365 .customFor(!HasCSSC, {{s32, s32}, {s64, s64}})
366 .customFor({{s128, s128},
367 {v4s16, v4s16},
368 {v8s16, v8s16},
369 {v2s32, v2s32},
370 {v4s32, v4s32},
371 {v2s64, v2s64}})
372 .clampScalar(0, s32, s128)
375 .minScalarEltSameAsIf(always, 1, 0)
376 .maxScalarEltSameAsIf(always, 1, 0)
377 .clampNumElements(0, v8s8, v16s8)
378 .clampNumElements(0, v4s16, v8s16)
379 .clampNumElements(0, v2s32, v4s32)
380 .clampNumElements(0, v2s64, v2s64)
383
384 getActionDefinitionsBuilder({G_CTLZ, G_CTLS})
385 .legalFor({{i32, i32},
386 {i64, i64},
387 {v8i8, v8i8},
388 {v16i8, v16i8},
389 {v4i16, v4i16},
390 {v8i16, v8i16},
391 {v2i32, v2i32},
392 {v4i32, v4i32}})
393 .widenScalarToNextPow2(1, /*Min=*/32)
394 .clampScalar(1, s32, s64)
396 .clampNumElements(0, v8s8, v16s8)
397 .clampNumElements(0, v4s16, v8s16)
398 .clampNumElements(0, v2s32, v4s32)
401 .scalarSameSizeAs(0, 1);
402
403 getActionDefinitionsBuilder(G_INSERT_SUBVECTOR).lower();
404
405 getActionDefinitionsBuilder(G_CTLZ_ZERO_POISON).lower();
406
408 .lowerIf(isVector(0))
409 .widenScalarToNextPow2(1, /*Min=*/32)
410 .clampScalar(1, s32, s64)
411 .scalarSameSizeAs(0, 1)
412 .legalFor(HasCSSC, {s32, s64})
413 .customFor(!HasCSSC, {s32, s64});
414
415 getActionDefinitionsBuilder(G_CTTZ_ZERO_POISON).lower();
416
417 getActionDefinitionsBuilder(G_BITREVERSE)
418 .legalFor({i32, i64, v8i8, v16i8})
419 .widenScalarToNextPow2(0, /*Min = */ 32)
421 .clampScalar(0, s32, s64)
422 .clampNumElements(0, v8s8, v16s8)
423 .clampNumElements(0, v4s16, v8s16)
424 .clampNumElements(0, v2s32, v4s32)
425 .clampNumElements(0, v2s64, v2s64)
428 .lower();
429
430 getActionDefinitionsBuilder(G_CLMUL).legalFor({v8i8, v16i8});
431
433 .legalFor({i32, i64, v4i16, v8i16, v2i32, v4i32, v2i64})
435 .clampScalar(0, s32, s64)
436 .clampNumElements(0, v4s16, v8s16)
437 .clampNumElements(0, v2s32, v4s32)
438 .clampNumElements(0, v2s64, v2s64)
440
441 getActionDefinitionsBuilder({G_UADDSAT, G_SADDSAT, G_USUBSAT, G_SSUBSAT})
442 .legalFor({v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
443 .legalFor(HasSVE, {nxv16i8, nxv8i16, nxv4i32, nxv2i64})
444 .clampNumElements(0, v8s8, v16s8)
445 .clampNumElements(0, v4s16, v8s16)
446 .clampNumElements(0, v2s32, v4s32)
447 .clampMaxNumElements(0, s64, 2)
450 .lower();
451
453 {G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FMA, G_FSQRT, G_FMAXNUM, G_FMINNUM,
454 G_FMAXIMUM, G_FMINIMUM, G_FCEIL, G_FFLOOR, G_FRINT, G_FNEARBYINT,
455 G_INTRINSIC_TRUNC, G_INTRINSIC_ROUND, G_INTRINSIC_ROUNDEVEN})
456 .legalFor({f32, f64, v2f32, v4f32, v2f64})
457 .legalFor(HasFP16, {f16, v4f16, v8f16})
458 .libcallFor({f128})
459 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
461 [=](const LegalityQuery &Q) {
462 return (!HasFP16 && Q.Types[0].getScalarType().isFloat16()) ||
463 Q.Types[0].getScalarType().isBFloat16();
464 },
465 changeElementTo(0, f32))
466 .clampNumElements(0, v4s16, v8s16)
467 .clampNumElements(0, v2s32, v4s32)
468 .clampNumElements(0, v2s64, v2s64)
470
471 getActionDefinitionsBuilder({G_FABS, G_FNEG})
472 .legalFor({f32, f64, v2f32, v4f32, v2f64})
473 .legalFor(HasFP16, {f16, bf16, v4f16, v4bf16, v8f16, v8bf16})
474 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
476 .clampNumElements(0, v4s16, v8s16)
477 .clampNumElements(0, v2s32, v4s32)
478 .clampNumElements(0, v2s64, v2s64)
480 .lowerFor({f16, bf16, v4f16, v4bf16, v8f16, v8bf16});
481
482 getActionDefinitionsBuilder({G_FREM, G_FCOS, G_FSIN, G_FPOW, G_FLOG, G_FLOG2,
483 G_FLOG10, G_FTAN, G_FEXP, G_FEXP2, G_FEXP10,
484 G_FACOS, G_FASIN, G_FATAN, G_FATAN2, G_FCOSH,
485 G_FSINH, G_FTANH, G_FMODF})
486 .libcallFor({f32, f64, f128})
487 .widenScalarFor({f16, bf16}, changeElementTo(0, f32))
488 .scalarize(0);
489 getActionDefinitionsBuilder({G_FPOWI, G_FLDEXP})
490 .libcallFor({{f32, i32}, {f64, i32}, {f128, i32}})
491 .widenScalarFor({f16, bf16}, changeElementTo(0, f32))
492 .scalarize(0);
493
494 getActionDefinitionsBuilder({G_LROUND, G_INTRINSIC_LRINT})
495 .legalFor({{i32, f32}, {i32, f64}, {i64, f32}, {i64, f64}})
496 .legalFor(HasFP16, {{i32, f16}, {i64, f16}})
497 .minScalar(1, s32)
498 .libcallFor({{s64, s128}})
499 .lower();
500 getActionDefinitionsBuilder({G_LLROUND, G_INTRINSIC_LLRINT})
501 .legalFor({{i64, f32}, {i64, f64}})
502 .legalFor(HasFP16, {{i64, f16}})
503 .minScalar(0, s64)
504 .minScalar(1, s32)
505 .libcallFor({{s64, s128}})
506 .lower();
507
508 // TODO: Custom legalization for mismatched types.
509 getActionDefinitionsBuilder(G_FCOPYSIGN)
511 [](const LegalityQuery &Query) { return Query.Types[0].isScalar(); },
512 [=](const LegalityQuery &Query) {
513 const LLT Ty = Query.Types[0];
514 return std::pair(0, LLT::fixed_vector(Ty == s16 ? 4 : 2, Ty));
515 })
516 .lower();
517
519
520 for (unsigned Op : {G_SEXTLOAD, G_ZEXTLOAD}) {
521 auto &Actions = getActionDefinitionsBuilder(Op);
522
523 if (Op == G_SEXTLOAD)
525
526 // Atomics have zero extending behavior.
527 Actions
528 .legalForTypesWithMemDesc({{s32, p0, s8, 8},
529 {s32, p0, s16, 8},
530 {s32, p0, s32, 8},
531 {s64, p0, s8, 2},
532 {s64, p0, s16, 2},
533 {s64, p0, s32, 4},
534 {s64, p0, s64, 8},
535 {p0, p0, s64, 8},
536 {v2s32, p0, s64, 8}})
537 .widenScalarToNextPow2(0)
538 .clampScalar(0, s32, s64)
539 // TODO: We could support sum-of-pow2's but the lowering code doesn't know
540 // how to do that yet.
541 .unsupportedIfMemSizeNotPow2()
542 // Lower anything left over into G_*EXT and G_LOAD
543 .lower();
544 }
545
546 auto IsPtrVecPred = [=](const LegalityQuery &Query) {
547 const LLT &ValTy = Query.Types[0];
548 return ValTy.isPointerVector() && ValTy.getAddressSpace() == 0;
549 };
550
552 .customIf([=](const LegalityQuery &Query) {
553 return HasRCPC3 && Query.Types[0] == s128 &&
554 Query.MMODescrs[0].Ordering == AtomicOrdering::Acquire;
555 })
556 .customIf([=](const LegalityQuery &Query) {
557 return Query.Types[0] == s128 &&
558 Query.MMODescrs[0].Ordering != AtomicOrdering::NotAtomic;
559 })
560 .legalForTypesWithMemDesc({{s8, p0, s8, 8},
561 {s16, p0, s16, 8},
562 {s32, p0, s32, 8},
563 {s64, p0, s64, 8},
564 {p0, p0, s64, 8},
565 {s128, p0, s128, 8},
566 {v8s8, p0, s64, 8},
567 {v16s8, p0, s128, 8},
568 {v4s16, p0, s64, 8},
569 {v8s16, p0, s128, 8},
570 {v2s32, p0, s64, 8},
571 {v4s32, p0, s128, 8},
572 {v2s64, p0, s128, 8}})
573 // These extends are also legal
574 .legalForTypesWithMemDesc(
575 {{s32, p0, s8, 8}, {s32, p0, s16, 8}, {s64, p0, s32, 8}})
576 .legalForTypesWithMemDesc({
577 // SVE vscale x 128 bit base sizes
578 {nxv16s8, p0, nxv16s8, 8},
579 {nxv8s16, p0, nxv8s16, 8},
580 {nxv4s32, p0, nxv4s32, 8},
581 {nxv2s64, p0, nxv2s64, 8},
582 })
583 .widenScalarToNextPow2(0, /* MinSize = */ 8)
584 .clampMaxNumElements(0, s8, 16)
585 .clampMaxNumElements(0, s16, 8)
586 .clampMaxNumElements(0, s32, 4)
587 .clampMaxNumElements(0, s64, 2)
588 .clampMaxNumElements(0, p0, 2)
590 .clampScalar(0, s8, s64)
592 [=](const LegalityQuery &Query) {
593 // Clamp extending load results to 32-bits.
594 return Query.Types[0].isScalar() &&
595 Query.Types[0] != Query.MMODescrs[0].MemoryTy &&
596 Query.Types[0].getSizeInBits() > 32;
597 },
598 changeTo(0, s32))
599 // TODO: Use BITCAST for v2i8, v2i16 after G_TRUNC gets sorted out
600 .bitcastIf(typeInSet(0, {v4s8}),
601 [=](const LegalityQuery &Query) {
602 const LLT VecTy = Query.Types[0];
603 return std::pair(0, LLT::integer(VecTy.getSizeInBits()));
604 })
605 .customIf(IsPtrVecPred)
606 .scalarizeIf(typeInSet(0, {v2s16, v2s8}), 0)
607 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
608
610 .customIf([=](const LegalityQuery &Query) {
611 return HasRCPC3 && Query.Types[0] == s128 &&
612 Query.MMODescrs[0].Ordering == AtomicOrdering::Release;
613 })
614 .customIf([=](const LegalityQuery &Query) {
615 return Query.Types[0] == s128 &&
616 Query.MMODescrs[0].Ordering != AtomicOrdering::NotAtomic;
617 })
618 .widenScalarIf(
619 all(scalarNarrowerThan(0, 32),
621 changeElementSizeTo(0, s32))
623 {{s8, p0, s8, 8}, {s16, p0, s8, 8}, // truncstorei8 from s16
624 {s32, p0, s8, 8}, // truncstorei8 from s32
625 {s64, p0, s8, 8}, // truncstorei8 from s64
626 {s16, p0, s16, 8}, {s32, p0, s16, 8}, // truncstorei16 from s32
627 {s64, p0, s16, 8}, // truncstorei16 from s64
628 {s32, p0, s8, 8}, {s32, p0, s16, 8}, {s32, p0, s32, 8},
629 {s64, p0, s64, 8}, {s64, p0, s32, 8}, // truncstorei32 from s64
630 {p0, p0, s64, 8}, {s128, p0, s128, 8}, {v16s8, p0, s128, 8},
631 {v8s8, p0, s64, 8}, {v4s16, p0, s64, 8}, {v8s16, p0, s128, 8},
632 {v2s32, p0, s64, 8}, {v4s32, p0, s128, 8}, {v2s64, p0, s128, 8}})
633 .legalForTypesWithMemDesc({
634 // SVE vscale x 128 bit base sizes
635 // TODO: Add nxv2p0. Consider bitcastIf.
636 // See #92130
637 // https://github.com/llvm/llvm-project/pull/92130#discussion_r1616888461
638 {nxv16s8, p0, nxv16s8, 8},
639 {nxv8s16, p0, nxv8s16, 8},
640 {nxv4s32, p0, nxv4s32, 8},
641 {nxv2s64, p0, nxv2s64, 8},
642 })
643 .clampScalar(0, s8, s64)
644 .minScalarOrElt(0, s8)
645 .lowerIf([=](const LegalityQuery &Query) {
646 return Query.Types[0].isScalar() &&
647 Query.Types[0] != Query.MMODescrs[0].MemoryTy;
648 })
649 // Maximum: sN * k = 128
650 .clampMaxNumElements(0, s8, 16)
651 .clampMaxNumElements(0, s16, 8)
652 .clampMaxNumElements(0, s32, 4)
653 .clampMaxNumElements(0, s64, 2)
654 .clampMaxNumElements(0, p0, 2)
656 // TODO: Use BITCAST for v2i8, v2i16 after G_TRUNC gets sorted out
657 .bitcastIf(all(typeInSet(0, {v4s8}),
658 LegalityPredicate([=](const LegalityQuery &Query) {
659 return Query.Types[0].getSizeInBits() ==
660 Query.MMODescrs[0].MemoryTy.getSizeInBits();
661 })),
662 [=](const LegalityQuery &Query) {
663 const LLT VecTy = Query.Types[0];
664 return std::pair(0, LLT::integer(VecTy.getSizeInBits()));
665 })
666 .customIf(IsPtrVecPred)
667 .scalarizeIf(typeInSet(0, {v2s16, v2s8}), 0)
668 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
669 .lower();
670
671 getActionDefinitionsBuilder(G_INDEXED_STORE)
672 // Idx 0 == Ptr, Idx 1 == Val
673 // TODO: we can implement legalizations but as of now these are
674 // generated in a very specific way.
676 {p0, s8, s8, 8},
677 {p0, s16, s16, 8},
678 {p0, s32, s8, 8},
679 {p0, s32, s16, 8},
680 {p0, s32, s32, 8},
681 {p0, s64, s64, 8},
682 {p0, p0, p0, 8},
683 {p0, v8s8, v8s8, 8},
684 {p0, v16s8, v16s8, 8},
685 {p0, v4s16, v4s16, 8},
686 {p0, v8s16, v8s16, 8},
687 {p0, v2s32, v2s32, 8},
688 {p0, v4s32, v4s32, 8},
689 {p0, v2s64, v2s64, 8},
690 {p0, v2p0, v2p0, 8},
691 {p0, s128, s128, 8},
692 })
693 .unsupported();
694
695 auto IndexedLoadBasicPred = [=](const LegalityQuery &Query) {
696 LLT LdTy = Query.Types[0];
697 LLT PtrTy = Query.Types[1];
698 if (!llvm::is_contained(PackedVectorAllTypesVec, LdTy) &&
699 !llvm::is_contained(ScalarAndPtrTypesVec, LdTy) && LdTy != s128)
700 return false;
701 if (PtrTy != p0)
702 return false;
703 return true;
704 };
705 getActionDefinitionsBuilder(G_INDEXED_LOAD)
708 .legalIf(IndexedLoadBasicPred)
709 .unsupported();
710 getActionDefinitionsBuilder({G_INDEXED_SEXTLOAD, G_INDEXED_ZEXTLOAD})
711 .unsupportedIf(
713 .legalIf(all(typeInSet(0, {s16, s32, s64}),
714 LegalityPredicate([=](const LegalityQuery &Q) {
715 LLT LdTy = Q.Types[0];
716 LLT PtrTy = Q.Types[1];
717 LLT MemTy = Q.MMODescrs[0].MemoryTy;
718 if (PtrTy != p0)
719 return false;
720 if (LdTy == s16)
721 return MemTy == s8;
722 if (LdTy == s32)
723 return MemTy == s8 || MemTy == s16;
724 if (LdTy == s64)
725 return MemTy == s8 || MemTy == s16 || MemTy == s32;
726 return false;
727 })))
728 .unsupported();
729
730 // Constants
732 .legalFor({p0, s8, s16, s32, s64})
733 .widenScalarToNextPow2(0)
734 .clampScalar(0, s8, s64);
735 getActionDefinitionsBuilder(G_FCONSTANT)
736 .legalFor({s16, s32, s64, s128});
737
738 // FIXME: fix moreElementsToNextPow2
740 .legalFor({{i32, i32}, {i32, i64}, {i32, p0}})
742 .minScalarOrElt(1, s8)
743 .clampScalar(1, s32, s64)
744 .clampScalar(0, s32, s32)
747 [=](const LegalityQuery &Query) {
748 const LLT &Ty = Query.Types[0];
749 const LLT &SrcTy = Query.Types[1];
750 return Ty.isVector() && !SrcTy.isPointerVector() &&
751 Ty.getElementType() != SrcTy.getElementType();
752 },
753 0, 1)
754 .minScalarOrEltIf(
755 [=](const LegalityQuery &Query) { return Query.Types[1] == v2s16; },
756 1, s32)
757 .minScalarOrEltIf(
758 [=](const LegalityQuery &Query) {
759 return Query.Types[1].isPointerVector();
760 },
761 0, s64)
763 .clampNumElements(1, v8s8, v16s8)
764 .clampNumElements(1, v4s16, v8s16)
765 .clampNumElements(1, v2s32, v4s32)
766 .clampNumElements(1, v2s64, v2s64)
767 .clampNumElements(1, v2p0, v2p0)
768 .customIf(isVector(0));
769
771 .legalFor({{i32, f32},
772 {i32, f64},
773 {v4i32, v4f32},
774 {v2i32, v2f32},
775 {v2i64, v2f64}})
776 .legalFor(HasFP16, {{i32, f16}, {v4i16, v4f16}, {v8i16, v8f16}})
778 .clampScalar(0, s32, s32)
780 [=](const LegalityQuery &Q) {
781 return (!HasFP16 && Q.Types[1].getScalarType().isFloat16()) ||
782 Q.Types[1].getScalarType().isBFloat16();
783 },
784 changeElementTo(1, f32))
785 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1)
787 [=](const LegalityQuery &Query) {
788 const LLT &Ty = Query.Types[0];
789 const LLT &SrcTy = Query.Types[1];
790 return Ty.isVector() && !SrcTy.isPointerVector() &&
791 Ty.getElementType() != SrcTy.getElementType();
792 },
793 0, 1)
794 .clampNumElements(1, v4s16, v8s16)
795 .clampNumElements(1, v2s32, v4s32)
796 .clampMaxNumElements(1, s64, 2)
798 .libcallFor({{s32, s128}});
799
800 // Extensions
801 auto ExtLegalFunc = [=](const LegalityQuery &Query) {
802 unsigned DstSize = Query.Types[0].getSizeInBits();
803
804 // Handle legal vectors using legalFor
805 if (Query.Types[0].isVector())
806 return false;
807
808 if (DstSize < 8 || DstSize >= 128 || !isPowerOf2_32(DstSize))
809 return false; // Extending to a scalar s128 needs narrowing.
810
811 const LLT &SrcTy = Query.Types[1];
812
813 // Make sure we fit in a register otherwise. Don't bother checking that
814 // the source type is below 128 bits. We shouldn't be allowing anything
815 // through which is wider than the destination in the first place.
816 unsigned SrcSize = SrcTy.getSizeInBits();
817 if (SrcSize < 8 || !isPowerOf2_32(SrcSize))
818 return false;
819
820 return true;
821 };
822 getActionDefinitionsBuilder({G_ZEXT, G_SEXT, G_ANYEXT})
823 .legalIf(ExtLegalFunc)
824 .legalFor({{v8s16, v8s8}, {v4s32, v4s16}, {v2s64, v2s32}})
825 .clampScalar(0, s64, s64) // Just for s128, others are handled above.
827 .clampMaxNumElements(1, s8, 8)
828 .clampMaxNumElements(1, s16, 4)
829 .clampMaxNumElements(1, s32, 2)
830 // Tries to convert a large EXTEND into two smaller EXTENDs
831 .lowerIf([=](const LegalityQuery &Query) {
832 return (Query.Types[0].getScalarSizeInBits() >
833 Query.Types[1].getScalarSizeInBits() * 2) &&
834 Query.Types[0].isVector() &&
835 (Query.Types[1].getScalarSizeInBits() == 8 ||
836 Query.Types[1].getScalarSizeInBits() == 16);
837 })
838 .clampMinNumElements(1, s8, 8)
839 .clampMinNumElements(1, s16, 4)
841
843 .legalFor({{v8s8, v8s16}, {v4s16, v4s32}, {v2s32, v2s64}})
845 .clampMaxNumElements(0, s8, 8)
846 .clampMaxNumElements(0, s16, 4)
847 .clampMaxNumElements(0, s32, 2)
849 [=](const LegalityQuery &Query) { return Query.Types[0].isVector(); },
850 0, s8)
851 .lowerIf([=](const LegalityQuery &Query) {
852 LLT DstTy = Query.Types[0];
853 LLT SrcTy = Query.Types[1];
854 return DstTy.isVector() && SrcTy.getSizeInBits() > 128 &&
855 DstTy.getScalarSizeInBits() * 2 <= SrcTy.getScalarSizeInBits();
856 })
857 .clampMinNumElements(0, s8, 8)
858 .clampMinNumElements(0, s16, 4)
859 .alwaysLegal();
860
861 getActionDefinitionsBuilder({G_TRUNC_SSAT_S, G_TRUNC_SSAT_U, G_TRUNC_USAT_U})
862 .legalFor({{v8i8, v8i16}, {v4i16, v4i32}, {v2i32, v2i64}})
863 .clampNumElements(0, v8s8, v8s8)
864 .clampNumElements(0, v4s16, v4s16)
865 .clampNumElements(0, v2s32, v2s32)
866 .lower();
867
868 getActionDefinitionsBuilder(G_SEXT_INREG)
869 .legalFor({i32, i64, v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
870 .maxScalar(0, s64)
871 .clampNumElements(0, v8s8, v16s8)
872 .clampNumElements(0, v4s16, v8s16)
873 .clampNumElements(0, v2s32, v4s32)
874 .clampMaxNumElements(0, s64, 2)
875 .lower();
876
877 // FP conversions
879 .legalFor(
880 {{f16, f32}, {f16, f64}, {f32, f64}, {v4f16, v4f32}, {v2f32, v2f64}})
881 .legalFor(ST.hasBF16(), {{bf16, f32}, {v4bf16, v4f32}})
882 .libcallFor({{f16, f128}, {f32, f128}, {f64, f128}})
884 .customIf([](const LegalityQuery &Q) {
885 LLT DstTy = Q.Types[0];
886 LLT SrcTy = Q.Types[1];
887 return SrcTy.getScalarSizeInBits() == 64 &&
888 DstTy.getScalarSizeInBits() == 16;
889 })
890 .lowerFor({{bf16, f32}, {v4bf16, v4f32}})
891 // Clamp based on input
892 .clampNumElements(1, v4s32, v4s32)
893 .clampNumElements(1, v2s64, v2s64)
894 .scalarize(0);
895
896 getActionDefinitionsBuilder(G_FPEXT)
897 .legalFor({{f32, f16},
898 {f64, f16},
899 {f32, bf16},
900 {f64, f32},
901 {v4f32, v4f16},
902 {v4f32, v4bf16},
903 {v2f64, v2f32}})
904 .libcallFor({{f128, f64}, {f128, f32}, {f128, f16}})
907 [](const LegalityQuery &Q) {
908 LLT DstTy = Q.Types[0];
909 LLT SrcTy = Q.Types[1];
910 return SrcTy.isVector() && DstTy.isVector() &&
911 SrcTy.getScalarSizeInBits() == 16 &&
912 DstTy.getScalarSizeInBits() == 64;
913 },
914 changeElementTo(1, f32))
915 .clampNumElements(0, v4s32, v4s32)
916 .clampNumElements(0, v2s64, v2s64)
917 .scalarize(0);
918
919 // Conversions
920 getActionDefinitionsBuilder({G_FPTOSI, G_FPTOUI})
921 .legalFor({{i32, f32},
922 {i64, f32},
923 {i32, f64},
924 {i64, f64},
925 {v2i32, v2f32},
926 {v4i32, v4f32},
927 {v2i64, v2f64}})
928 .legalFor(HasFP16,
929 {{i32, f16}, {i64, f16}, {v4i16, v4f16}, {v8i16, v8f16}})
930 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
932 // The range of a fp16 value fits into an i17, so we can lower the width
933 // to i64.
935 [=](const LegalityQuery &Query) {
936 return Query.Types[1] == f16 && Query.Types[0].getSizeInBits() > 64;
937 },
938 changeTo(0, i64))
941 .minScalar(0, s32)
943 [HasFP16](const LegalityQuery &Query) {
944 return (!HasFP16 && Query.Types[1].getScalarType().isFloat16()) ||
945 Query.Types[1].getScalarType().isBFloat16();
946 },
947 changeElementTo(1, f32))
948 .widenScalarIf(
949 [=](const LegalityQuery &Query) {
950 return Query.Types[0].getScalarSizeInBits() <= 64 &&
951 Query.Types[0].getScalarSizeInBits() >
952 Query.Types[1].getScalarSizeInBits();
953 },
955 .widenScalarIf(
956 [=](const LegalityQuery &Query) {
957 return Query.Types[1].getScalarSizeInBits() <= 64 &&
958 Query.Types[0].getScalarSizeInBits() <
959 Query.Types[1].getScalarSizeInBits();
960 },
962 .clampNumElements(0, v4s16, v8s16)
963 .clampNumElements(0, v2s32, v4s32)
964 .clampMaxNumElements(0, s64, 2)
965 .libcallFor(
966 {{i32, f128}, {i64, f128}, {i128, f128}, {i128, f32}, {i128, f64}});
967
968 getActionDefinitionsBuilder({G_FPTOSI_SAT, G_FPTOUI_SAT})
969 .legalFor({{i32, f32},
970 {i64, f32},
971 {i32, f64},
972 {i64, f64},
973 {v2i32, v2f32},
974 {v4i32, v4f32},
975 {v2i64, v2f64}})
976 .legalFor(
977 HasFP16,
978 {{i16, f16}, {i32, f16}, {i64, f16}, {v4i16, v4f16}, {v8i16, v8f16}})
979 // Handle types larger than i64 by scalarizing/lowering.
980 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
982 // The range of a fp16 value fits into an i17, so we can lower the width
983 // to i64.
985 [=](const LegalityQuery &Query) {
986 return Query.Types[1] == f16 && Query.Types[0].getSizeInBits() > 64;
987 },
988 changeTo(0, i64))
989 .lowerIf(::any(scalarWiderThan(0, 64), scalarWiderThan(1, 64)), 0)
991 .widenScalarToNextPow2(0, /*MinSize=*/32)
992 .minScalar(0, s32)
994 [HasFP16](const LegalityQuery &Query) {
995 return (!HasFP16 && Query.Types[1].getScalarType().isFloat16()) ||
996 Query.Types[1].getScalarType().isBFloat16();
997 },
998 changeElementTo(1, f32))
999 .widenScalarIf(
1000 [=](const LegalityQuery &Query) {
1001 unsigned ITySize = Query.Types[0].getScalarSizeInBits();
1002 return (ITySize == 16 || ITySize == 32 || ITySize == 64) &&
1003 ITySize > Query.Types[1].getScalarSizeInBits();
1004 },
1006 .widenScalarIf(
1007 [=](const LegalityQuery &Query) {
1008 unsigned FTySize = Query.Types[1].getScalarSizeInBits();
1009 return (FTySize == 16 || FTySize == 32 || FTySize == 64) &&
1010 Query.Types[0].getScalarSizeInBits() < FTySize;
1011 },
1014 .clampNumElements(0, v4s16, v8s16)
1015 .clampNumElements(0, v2s32, v4s32)
1016 .clampMaxNumElements(0, s64, 2);
1017
1018 getActionDefinitionsBuilder({G_SITOFP, G_UITOFP})
1019 .legalFor({{f32, i32},
1020 {f64, i32},
1021 {f32, i64},
1022 {f64, i64},
1023 {v2f32, v2i32},
1024 {v4f32, v4i32},
1025 {v2f64, v2i64}})
1026 .legalFor(HasFP16,
1027 {{f16, i32}, {f16, i64}, {v4f16, v4i16}, {v8f16, v8i16}})
1028 .unsupportedIf([&](const LegalityQuery &Query) {
1029 return Query.Types[0].getScalarType().isBFloat16();
1030 })
1031 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1)
1035 .minScalar(1, f32)
1036 .lowerIf([](const LegalityQuery &Query) {
1037 return Query.Types[1].isVector() &&
1038 Query.Types[1].getScalarSizeInBits() == 64 &&
1039 Query.Types[0].getScalarSizeInBits() == 16;
1040 })
1041 .widenScalarOrEltToNextPow2OrMinSize(0, /*MinSize=*/HasFP16 ? 16 : 32)
1042 .scalarizeIf(
1043 // v2i64->v2f32 needs to scalarize to avoid double-rounding issues.
1044 [](const LegalityQuery &Query) {
1045 return Query.Types[0].getScalarSizeInBits() == 32 &&
1046 Query.Types[1].getScalarSizeInBits() == 64;
1047 },
1048 0)
1049 .widenScalarIf(
1050 [](const LegalityQuery &Query) {
1051 return Query.Types[1].getScalarSizeInBits() <= 64 &&
1052 Query.Types[0].getScalarSizeInBits() <
1053 Query.Types[1].getScalarSizeInBits();
1054 },
1056 .widenScalarIf(
1057 [](const LegalityQuery &Query) {
1058 return Query.Types[0].getScalarSizeInBits() <= 64 &&
1059 Query.Types[0].getScalarSizeInBits() >
1060 Query.Types[1].getScalarSizeInBits();
1061 },
1063 .clampNumElements(0, v4s16, v8s16)
1064 .clampNumElements(0, v2s32, v4s32)
1065 .clampMaxNumElements(0, s64, 2)
1066 .libcallFor({{f16, i128},
1067 {f32, i128},
1068 {f64, i128},
1069 {f128, i128},
1070 {f128, i32},
1071 {f128, i64}});
1072
1073 // Control-flow
1074 getActionDefinitionsBuilder(G_BR).alwaysLegal();
1075 getActionDefinitionsBuilder(G_BRCOND)
1076 .legalFor({s32})
1077 .clampScalar(0, s32, s32);
1078 getActionDefinitionsBuilder(G_BRINDIRECT).legalFor({p0});
1079
1080 getActionDefinitionsBuilder(G_SELECT)
1081 .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
1082 .widenScalarToNextPow2(0)
1083 .clampScalar(0, s32, s64)
1084 .clampScalar(1, s32, s32)
1087 .lowerIf(isVector(0));
1088
1089 // Pointer-handling
1090 getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
1091
1092 if (TM.getCodeModel() == CodeModel::Small)
1093 getActionDefinitionsBuilder(G_GLOBAL_VALUE).custom();
1094 else
1095 getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0});
1096
1097 getActionDefinitionsBuilder(G_PTRAUTH_GLOBAL_VALUE)
1098 .legalIf(all(typeIs(0, p0), typeIs(1, p0)));
1099
1100 getActionDefinitionsBuilder(G_PTRTOINT)
1101 .legalFor({{i64, p0}, {v2i64, v2p0}})
1102 .widenScalarToNextPow2(0, 64)
1103 .clampScalar(0, s64, s64)
1104 .clampMaxNumElements(0, s64, 2);
1105
1106 getActionDefinitionsBuilder(G_INTTOPTR)
1107 .unsupportedIf([&](const LegalityQuery &Query) {
1108 return Query.Types[0].getSizeInBits() != Query.Types[1].getSizeInBits();
1109 })
1110 .legalFor({{p0, i64}, {v2p0, v2i64}})
1111 .clampMaxNumElements(1, s64, 2);
1112
1113 // Casts for 32 and 64-bit width type are just copies.
1114 // Same for 128-bit width type, except they are on the FPR bank.
1115 getActionDefinitionsBuilder(G_BITCAST)
1117 // Keeping 32-bit instructions legal to prevent regression in some tests
1118 .legalForCartesianProduct({s32, v2s16, v4s8})
1119 .legalForCartesianProduct({s64, v8s8, v4s16, v2s32})
1120 .legalForCartesianProduct({s128, v16s8, v8s16, v4s32, v2s64, v2p0})
1121 .customIf([=](const LegalityQuery &Query) {
1122 // Handle casts from i1 vectors to scalars.
1123 LLT DstTy = Query.Types[0];
1124 LLT SrcTy = Query.Types[1];
1125 return DstTy.isScalar() && SrcTy.isVector() &&
1126 SrcTy.getScalarSizeInBits() == 1;
1127 })
1128 .lowerIf([=](const LegalityQuery &Query) {
1129 return Query.Types[0].isVector() != Query.Types[1].isVector();
1130 })
1132 .clampNumElements(0, v8s8, v16s8)
1133 .clampNumElements(0, v4s16, v8s16)
1134 .clampNumElements(0, v2s32, v4s32)
1135 .clampMaxNumElements(0, s64, 2)
1136 .lower();
1137
1138 getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
1139
1140 // va_list must be a pointer, but most sized types are pretty easy to handle
1141 // as the destination.
1142 getActionDefinitionsBuilder(G_VAARG)
1143 .customForCartesianProduct({s8, s16, s32, s64, p0}, {p0})
1144 .clampScalar(0, s8, s64)
1145 .widenScalarToNextPow2(0, /*Min*/ 8);
1146
1147 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG_WITH_SUCCESS)
1148 .lowerIf(
1149 all(typeInSet(0, {s8, s16, s32, s64, s128}), typeIs(2, p0)));
1150
1151 bool UseOutlineAtomics = ST.outlineAtomics() && !ST.hasLSE();
1152
1153 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG)
1154 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1155 .customFor(!UseOutlineAtomics, {{s128, p0}})
1156 .libcallFor(UseOutlineAtomics,
1157 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}, {s128, p0}})
1158 .clampScalar(0, s32, s64);
1159
1160 getActionDefinitionsBuilder({G_ATOMICRMW_XCHG, G_ATOMICRMW_ADD,
1161 G_ATOMICRMW_SUB, G_ATOMICRMW_AND, G_ATOMICRMW_OR,
1162 G_ATOMICRMW_XOR})
1163 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1164 .libcallFor(UseOutlineAtomics,
1165 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}})
1166 .clampScalar(0, s32, s64);
1167
1168 // Do not outline these atomics operations, as per comment in
1169 // AArch64ISelLowering.cpp's shouldExpandAtomicRMWInIR().
1170 getActionDefinitionsBuilder(
1171 {G_ATOMICRMW_MIN, G_ATOMICRMW_MAX, G_ATOMICRMW_UMIN, G_ATOMICRMW_UMAX})
1172 .legalIf(all(typeInSet(0, {s32, s64}), typeIs(1, p0)))
1173 .clampScalar(0, s32, s64);
1174
1175 getActionDefinitionsBuilder(G_BLOCK_ADDR).legalFor({p0});
1176
1177 // Merge/Unmerge
1178 for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) {
1179 unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1;
1180 unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0;
1181 getActionDefinitionsBuilder(Op)
1182 .widenScalarToNextPow2(LitTyIdx, 8)
1183 .widenScalarToNextPow2(BigTyIdx, 32)
1184 .clampScalar(LitTyIdx, s8, s64)
1185 .clampScalar(BigTyIdx, s32, s128)
1186 .legalIf([=](const LegalityQuery &Q) {
1187 switch (Q.Types[BigTyIdx].getSizeInBits()) {
1188 case 32:
1189 case 64:
1190 case 128:
1191 break;
1192 default:
1193 return false;
1194 }
1195 switch (Q.Types[LitTyIdx].getSizeInBits()) {
1196 case 8:
1197 case 16:
1198 case 32:
1199 case 64:
1200 return true;
1201 default:
1202 return false;
1203 }
1204 });
1205 }
1206
1207 // TODO : nxv4s16, nxv2s16, nxv2s32
1208 getActionDefinitionsBuilder(G_EXTRACT_VECTOR_ELT)
1209 .legalFor(HasSVE, {{s16, nxv16s8, s64},
1210 {s16, nxv8s16, s64},
1211 {s32, nxv4s32, s64},
1212 {s64, nxv2s64, s64}})
1213 .unsupportedIf([=](const LegalityQuery &Query) {
1214 const LLT &EltTy = Query.Types[1].getElementType();
1215 if (Query.Types[1].isScalableVector())
1216 return false;
1217 return Query.Types[0] != EltTy;
1218 })
1219 .minScalar(2, s64)
1220 .customIf([=](const LegalityQuery &Query) {
1221 const LLT &VecTy = Query.Types[1];
1222 return VecTy == v8s8 || VecTy == v16s8 || VecTy == v2s16 ||
1223 VecTy == v4s16 || VecTy == v8s16 || VecTy == v2s32 ||
1224 VecTy == v4s32 || VecTy == v2s64 || VecTy == v2p0;
1225 })
1226 .minScalarOrEltIf(
1227 [=](const LegalityQuery &Query) {
1228 // We want to promote to <M x s1> to <M x s64> if that wouldn't
1229 // cause the total vec size to be > 128b.
1230 return Query.Types[1].isFixedVector() &&
1231 Query.Types[1].getNumElements() <= 2;
1232 },
1233 0, s64)
1234 .minScalarOrEltIf(
1235 [=](const LegalityQuery &Query) {
1236 return Query.Types[1].isFixedVector() &&
1237 Query.Types[1].getNumElements() <= 4;
1238 },
1239 0, s32)
1240 .minScalarOrEltIf(
1241 [=](const LegalityQuery &Query) {
1242 return Query.Types[1].isFixedVector() &&
1243 Query.Types[1].getNumElements() <= 8;
1244 },
1245 0, s16)
1246 .minScalarOrEltIf(
1247 [=](const LegalityQuery &Query) {
1248 return Query.Types[1].isFixedVector() &&
1249 Query.Types[1].getNumElements() <= 16;
1250 },
1251 0, s8)
1252 .minScalarOrElt(0, s8) // Worst case, we need at least s8.
1253 .moreElementsToNextPow2(1)
1254 .clampMaxNumElements(1, s64, 2)
1255 .clampMaxNumElements(1, s32, 4)
1256 .clampMaxNumElements(1, s16, 8)
1257 .clampMaxNumElements(1, s8, 16)
1258 .clampMaxNumElements(1, p0, 2)
1259 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1);
1260
1261 getActionDefinitionsBuilder(G_INSERT_VECTOR_ELT)
1262 .legalIf(
1263 typeInSet(0, {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64, v2p0}))
1264 .legalFor(HasSVE, {{nxv16s8, s32, s64},
1265 {nxv8s16, s32, s64},
1266 {nxv4s32, s32, s64},
1267 {nxv2s64, s64, s64}})
1269 .widenVectorEltsToVectorMinSize(0, 64)
1270 .clampNumElements(0, v8s8, v16s8)
1271 .clampNumElements(0, v4s16, v8s16)
1272 .clampNumElements(0, v2s32, v4s32)
1273 .clampMaxNumElements(0, s64, 2)
1274 .clampMaxNumElements(0, p0, 2)
1275 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
1276
1277 getActionDefinitionsBuilder(G_BUILD_VECTOR)
1278 .legalFor({{v8s8, s8},
1279 {v16s8, s8},
1280 {v4s16, s16},
1281 {v8s16, s16},
1282 {v2s32, s32},
1283 {v4s32, s32},
1284 {v2s64, s64},
1285 {v2p0, p0}})
1286 .clampNumElements(0, v4s32, v4s32)
1287 .clampNumElements(0, v2s64, v2s64)
1288 .minScalarOrElt(0, s8)
1289 .widenVectorEltsToVectorMinSize(0, 64)
1290 .widenScalarOrEltToNextPow2(0)
1291 .minScalarSameAs(1, 0);
1292
1293 getActionDefinitionsBuilder(G_BUILD_VECTOR_TRUNC).lower();
1294
1295 getActionDefinitionsBuilder(G_SHUFFLE_VECTOR)
1296 .legalIf([=](const LegalityQuery &Query) {
1297 const LLT &DstTy = Query.Types[0];
1298 const LLT &SrcTy = Query.Types[1];
1299 // For now just support the TBL2 variant which needs the source vectors
1300 // to be the same size as the dest.
1301 if (DstTy != SrcTy)
1302 return false;
1303 return llvm::is_contained(
1304 {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64}, DstTy);
1305 })
1306 .moreElementsIf(
1307 [](const LegalityQuery &Query) {
1308 return Query.Types[0].getNumElements() >
1309 Query.Types[1].getNumElements();
1310 },
1311 changeTo(1, 0))
1313 .moreElementsIf(
1314 [](const LegalityQuery &Query) {
1315 return Query.Types[0].getNumElements() <
1316 Query.Types[1].getNumElements();
1317 },
1318 changeTo(0, 1))
1319 .widenScalarOrEltToNextPow2OrMinSize(0, 8)
1320 .clampNumElements(0, v8s8, v16s8)
1321 .clampNumElements(0, v4s16, v8s16)
1322 .clampNumElements(0, v4s32, v4s32)
1323 .clampNumElements(0, v2s64, v2s64)
1324 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
1325 .bitcastIf(isPointerVector(0), [=](const LegalityQuery &Query) {
1326 // Bitcast pointers vector to i64.
1327 const LLT DstTy = Query.Types[0];
1328 return std::pair(
1329 0, LLT::vector(DstTy.getElementCount(), LLT::integer(64)));
1330 });
1331
1332 getActionDefinitionsBuilder(G_CONCAT_VECTORS)
1333 .legalFor({{v16s8, v8s8}, {v8s16, v4s16}, {v4s32, v2s32}})
1334 .customIf([=](const LegalityQuery &Query) {
1335 return Query.Types[0].isFixedVector() &&
1336 Query.Types[0].getScalarSizeInBits() < 8;
1337 })
1338 .bitcastIf(
1339 [=](const LegalityQuery &Query) {
1340 return Query.Types[0].isFixedVector() &&
1341 Query.Types[1].isFixedVector() &&
1342 Query.Types[0].getScalarSizeInBits() >= 8 &&
1343 isPowerOf2_64(Query.Types[0].getScalarSizeInBits()) &&
1344 Query.Types[0].getSizeInBits() <= 128 &&
1345 Query.Types[1].getSizeInBits() <= 64;
1346 },
1347 [=](const LegalityQuery &Query) {
1348 const LLT DstTy = Query.Types[0];
1349 const LLT SrcTy = Query.Types[1];
1350 return std::pair(
1351 0, DstTy.changeElementSize(SrcTy.getSizeInBits())
1354 SrcTy.getNumElements())));
1355 });
1356
1357 getActionDefinitionsBuilder(G_EXTRACT_SUBVECTOR)
1358 .legalFor({{v8s8, v16s8}, {v4s16, v8s16}, {v2s32, v4s32}})
1360 .clampMaxNumElements(0, s8, 16)
1361 .clampMaxNumElements(0, s16, 8)
1362 .clampMaxNumElements(0, s32, 4)
1363 .clampNumElements(1, v8s8, v16s8)
1364 .clampNumElements(1, v4s16, v8s16)
1365 .clampNumElements(1, v2s32, v4s32)
1366 .lower()
1367 .immIdx(0); // Inform verifier imm idx 0 is handled.
1368
1369 // TODO: {nxv16s8, s8}, {nxv8s16, s16}
1370 getActionDefinitionsBuilder(G_SPLAT_VECTOR)
1371 .legalFor(HasSVE, {{nxv4s32, s32}, {nxv2s64, s64}});
1372
1373 getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
1374
1375 getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, s64}});
1376
1377 getActionDefinitionsBuilder({G_TRAP, G_DEBUGTRAP, G_UBSANTRAP}).alwaysLegal();
1378
1379 getActionDefinitionsBuilder(G_DYN_STACKALLOC).custom();
1380
1381 getActionDefinitionsBuilder({G_STACKSAVE, G_STACKRESTORE}).lower();
1382
1383 if (ST.hasMOPS()) {
1384 // G_BZERO is not supported. Currently it is only emitted by
1385 // PreLegalizerCombiner for G_MEMSET with zero constant.
1386 getActionDefinitionsBuilder(G_BZERO).unsupported();
1387
1388 getActionDefinitionsBuilder(G_MEMSET)
1389 .legalForCartesianProduct({p0}, {s64}, {s64})
1390 .customForCartesianProduct({p0}, {s8}, {s64})
1391 .immIdx(0); // Inform verifier imm idx 0 is handled.
1392
1393 getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
1394 .legalForCartesianProduct({p0}, {p0}, {s64})
1395 .immIdx(0); // Inform verifier imm idx 0 is handled.
1396
1397 // G_MEMCPY_INLINE does not have a tailcall immediate
1398 getActionDefinitionsBuilder(G_MEMCPY_INLINE)
1399 .legalForCartesianProduct({p0}, {p0}, {s64});
1400
1401 getActionDefinitionsBuilder(G_MEMSET_INLINE)
1402 .legalForCartesianProduct({p0}, {s64}, {s64})
1403 .customForCartesianProduct({p0}, {s8}, {s64});
1404 } else {
1405 getActionDefinitionsBuilder({G_BZERO, G_MEMCPY, G_MEMMOVE, G_MEMSET})
1406 .libcall();
1407 }
1408
1409 // For fadd reductions we have pairwise operations available. We treat the
1410 // usual legal types as legal and handle the lowering to pairwise instructions
1411 // later.
1412 getActionDefinitionsBuilder(G_VECREDUCE_FADD)
1413 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1414 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
1415 .widenScalarIf(
1416 [HasFP16](const LegalityQuery &Query) {
1417 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1418 Query.Types[0].getScalarType().isBFloat16();
1419 },
1420 changeElementTo(0, f32))
1421 .clampMaxNumElements(1, s64, 2)
1422 .clampMaxNumElements(1, s32, 4)
1423 .clampMaxNumElements(1, s16, 8)
1424 .moreElementsToNextPow2(1)
1425 .scalarize(1)
1426 .lower();
1427
1428 // For fmul reductions we need to split up into individual operations. We
1429 // clamp to 128 bit vectors then to 64bit vectors to produce a cascade of
1430 // smaller types, followed by scalarizing what remains.
1431 getActionDefinitionsBuilder(G_VECREDUCE_FMUL)
1432 .widenScalarIf(
1433 [HasFP16](const LegalityQuery &Query) {
1434 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1435 Query.Types[0].getScalarType().isBFloat16();
1436 },
1437 changeElementTo(0, f32))
1438 .clampMaxNumElements(1, s64, 2)
1439 .clampMaxNumElements(1, s32, 4)
1440 .clampMaxNumElements(1, s16, 8)
1441 .clampMaxNumElements(1, s32, 2)
1442 .clampMaxNumElements(1, s16, 4)
1443 .scalarize(1)
1444 .lower();
1445
1446 getActionDefinitionsBuilder({G_VECREDUCE_SEQ_FADD, G_VECREDUCE_SEQ_FMUL})
1447 .scalarize(2)
1448 .lower();
1449
1450 getActionDefinitionsBuilder(G_VECREDUCE_ADD)
1451 .legalFor({{i8, v8i8},
1452 {i8, v16i8},
1453 {i16, v4i16},
1454 {i16, v8i16},
1455 {i32, v2i32},
1456 {i32, v4i32},
1457 {i64, v2i64}})
1459 .clampMaxNumElements(1, s64, 2)
1460 .clampMaxNumElements(1, s32, 4)
1461 .clampMaxNumElements(1, s16, 8)
1462 .clampMaxNumElements(1, s8, 16)
1463 .widenVectorEltsToVectorMinSize(1, 64)
1464 .scalarize(1);
1465
1466 getActionDefinitionsBuilder({G_VECREDUCE_FMIN, G_VECREDUCE_FMAX,
1467 G_VECREDUCE_FMINIMUM, G_VECREDUCE_FMAXIMUM})
1468 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1469 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
1470 .widenScalarIf(
1471 [HasFP16](const LegalityQuery &Query) {
1472 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1473 Query.Types[0].getScalarType().isBFloat16();
1474 },
1475 changeElementTo(0, f32))
1476 .clampMaxNumElements(1, s64, 2)
1477 .clampMaxNumElements(1, s32, 4)
1478 .clampMaxNumElements(1, s16, 8)
1479 .scalarize(1)
1480 .lower();
1481
1482 getActionDefinitionsBuilder(G_VECREDUCE_MUL)
1483 .clampMaxNumElements(1, s32, 2)
1484 .clampMaxNumElements(1, s16, 4)
1485 .clampMaxNumElements(1, s8, 8)
1486 .scalarize(1)
1487 .lower();
1488
1489 getActionDefinitionsBuilder(
1490 {G_VECREDUCE_SMIN, G_VECREDUCE_SMAX, G_VECREDUCE_UMIN, G_VECREDUCE_UMAX})
1491 .legalFor({{i8, v8i8},
1492 {i8, v16i8},
1493 {i16, v4i16},
1494 {i16, v8i16},
1495 {i32, v2i32},
1496 {i32, v4i32}})
1497 .moreElementsIf(
1498 [=](const LegalityQuery &Query) {
1499 return Query.Types[1].isVector() &&
1500 Query.Types[1].getElementType() != s8 &&
1501 Query.Types[1].getNumElements() & 1;
1502 },
1504 .clampMaxNumElements(1, s64, 2)
1505 .clampMaxNumElements(1, s32, 4)
1506 .clampMaxNumElements(1, s16, 8)
1507 .clampMaxNumElements(1, s8, 16)
1508 .scalarize(1)
1509 .lower();
1510
1511 getActionDefinitionsBuilder(
1512 {G_VECREDUCE_OR, G_VECREDUCE_AND, G_VECREDUCE_XOR})
1513 // Try to break down into smaller vectors as long as they're at least 64
1514 // bits. This lets us use vector operations for some parts of the
1515 // reduction.
1516 .fewerElementsIf(
1517 [=](const LegalityQuery &Q) {
1518 LLT SrcTy = Q.Types[1];
1519 if (SrcTy.isScalar())
1520 return false;
1521 if (!isPowerOf2_32(SrcTy.getNumElements()))
1522 return false;
1523 // We can usually perform 64b vector operations.
1524 return SrcTy.getSizeInBits() > 64;
1525 },
1526 [=](const LegalityQuery &Q) {
1527 LLT SrcTy = Q.Types[1];
1528 return std::make_pair(1, SrcTy.divide(2));
1529 })
1530 .scalarize(1)
1531 .lower();
1532
1533 // TODO: Update this to correct handling when adding AArch64/SVE support.
1534 getActionDefinitionsBuilder(G_VECTOR_COMPRESS).lower();
1535
1536 // Access to floating-point environment.
1537 getActionDefinitionsBuilder({G_GET_FPENV, G_SET_FPENV, G_RESET_FPENV,
1538 G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE})
1539 .libcall();
1540
1541 getActionDefinitionsBuilder({G_GET_ROUNDING, G_SET_ROUNDING})
1542 .customFor({s32});
1543
1544 getActionDefinitionsBuilder(G_IS_FPCLASS).lower();
1545
1546 getActionDefinitionsBuilder(G_PREFETCH).custom();
1547
1548 getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
1549
1550 getActionDefinitionsBuilder({G_INTRINSIC, G_INTRINSIC_W_SIDE_EFFECTS})
1551 .alwaysLegal();
1552 getActionDefinitionsBuilder(G_FENCE).alwaysLegal();
1553 getActionDefinitionsBuilder(G_INVOKE_REGION_START).alwaysLegal();
1554
1555 verify(*ST.getInstrInfo());
1556}
1557
1560 LostDebugLocObserver &LocObserver) const {
1561 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
1562 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
1563 GISelChangeObserver &Observer = Helper.Observer;
1564 switch (MI.getOpcode()) {
1565 default:
1566 // No idea what to do.
1567 return false;
1568 case TargetOpcode::G_VAARG:
1569 return legalizeVaArg(MI, MRI, MIRBuilder);
1570 case TargetOpcode::G_LOAD:
1571 case TargetOpcode::G_STORE:
1572 return legalizeLoadStore(MI, MRI, MIRBuilder, Observer);
1573 case TargetOpcode::G_SHL:
1574 case TargetOpcode::G_ASHR:
1575 case TargetOpcode::G_LSHR:
1576 return legalizeShlAshrLshr(MI, MRI, MIRBuilder, Observer);
1577 case TargetOpcode::G_GLOBAL_VALUE:
1578 return legalizeSmallCMGlobalValue(MI, MRI, MIRBuilder, Observer);
1579 case TargetOpcode::G_SBFX:
1580 case TargetOpcode::G_UBFX:
1581 return legalizeBitfieldExtract(MI, MRI, Helper);
1582 case TargetOpcode::G_FSHL:
1583 case TargetOpcode::G_FSHR:
1584 return legalizeFunnelShift(MI, MRI, MIRBuilder, Observer, Helper);
1585 case TargetOpcode::G_ROTR:
1586 return legalizeRotate(MI, MRI, Helper);
1587 case TargetOpcode::G_CTPOP:
1588 return legalizeCTPOP(MI, MRI, Helper);
1589 case TargetOpcode::G_ATOMIC_CMPXCHG:
1590 return legalizeAtomicCmpxchg128(MI, MRI, Helper);
1591 case TargetOpcode::G_CTTZ:
1592 return legalizeCTTZ(MI, Helper);
1593 case TargetOpcode::G_BZERO:
1594 case TargetOpcode::G_MEMCPY:
1595 case TargetOpcode::G_MEMMOVE:
1596 case TargetOpcode::G_MEMSET:
1597 case TargetOpcode::G_MEMSET_INLINE:
1598 return legalizeMemOps(MI, Helper);
1599 case TargetOpcode::G_EXTRACT_VECTOR_ELT:
1600 return legalizeExtractVectorElt(MI, MRI, Helper);
1601 case TargetOpcode::G_DYN_STACKALLOC:
1602 return legalizeDynStackAlloc(MI, Helper);
1603 case TargetOpcode::G_PREFETCH:
1604 return legalizePrefetch(MI, Helper);
1605 case TargetOpcode::G_ABS:
1606 return Helper.lowerAbsToCNeg(MI);
1607 case TargetOpcode::G_ICMP:
1608 return legalizeICMP(MI, MRI, MIRBuilder);
1609 case TargetOpcode::G_BITCAST:
1610 return legalizeBitcast(MI, Helper);
1611 case TargetOpcode::G_CONCAT_VECTORS:
1612 return legalizeConcatVectors(MI, MRI, MIRBuilder);
1613 case TargetOpcode::G_FPTRUNC:
1614 // In order to lower f16 to f64 properly, we need to use f32 as an
1615 // intermediary
1616 return legalizeFptrunc(MI, MIRBuilder, MRI);
1617 case TargetOpcode::G_GET_ROUNDING:
1618 return legalizeGetRounding(MI, MIRBuilder, MRI, Helper);
1619 case TargetOpcode::G_SET_ROUNDING:
1620 return legalizeSetRounding(MI, MIRBuilder, MRI, Helper);
1621 }
1622
1623 llvm_unreachable("expected switch to return");
1624}
1625
1626bool AArch64LegalizerInfo::legalizeBitcast(MachineInstr &MI,
1627 LegalizerHelper &Helper) const {
1628 assert(MI.getOpcode() == TargetOpcode::G_BITCAST && "Unexpected opcode");
1629 auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
1630 // We're trying to handle casts from i1 vectors to scalars but reloading from
1631 // stack.
1632 if (!DstTy.isScalar() || !SrcTy.isVector() ||
1633 SrcTy.getElementType() != LLT::scalar(1))
1634 return false;
1635
1636 Helper.createStackStoreLoad(DstReg, SrcReg);
1637 MI.eraseFromParent();
1638 return true;
1639}
1640
1641bool AArch64LegalizerInfo::legalizeFunnelShift(MachineInstr &MI,
1643 MachineIRBuilder &MIRBuilder,
1644 GISelChangeObserver &Observer,
1645 LegalizerHelper &Helper) const {
1646 assert(MI.getOpcode() == TargetOpcode::G_FSHL ||
1647 MI.getOpcode() == TargetOpcode::G_FSHR);
1648
1649 // Keep as G_FSHR if shift amount is a G_CONSTANT, else use generic
1650 // lowering
1651 Register ShiftNo = MI.getOperand(3).getReg();
1652 LLT ShiftTy = MRI.getType(ShiftNo);
1653 auto VRegAndVal = getIConstantVRegValWithLookThrough(ShiftNo, MRI);
1654
1655 // Adjust shift amount according to Opcode (FSHL/FSHR)
1656 // Convert FSHL to FSHR
1657 LLT OperationTy = MRI.getType(MI.getOperand(0).getReg());
1658 APInt BitWidth(ShiftTy.getSizeInBits(), OperationTy.getSizeInBits(), false);
1659
1660 // Lower non-constant shifts and leave zero shifts to the optimizer.
1661 if (!VRegAndVal || VRegAndVal->Value.urem(BitWidth) == 0)
1662 return (Helper.lowerFunnelShiftAsShifts(MI) ==
1664
1665 APInt Amount = VRegAndVal->Value.urem(BitWidth);
1666
1667 Amount = MI.getOpcode() == TargetOpcode::G_FSHL ? BitWidth - Amount : Amount;
1668
1669 // If the instruction is G_FSHR, has a 64-bit G_CONSTANT for shift amount
1670 // in the range of 0 <-> BitWidth, it is legal
1671 if (ShiftTy.getSizeInBits() == 64 && MI.getOpcode() == TargetOpcode::G_FSHR &&
1672 VRegAndVal->Value.ult(BitWidth))
1673 return true;
1674
1675 // Cast the ShiftNumber to a 64-bit type
1676 auto Cast64 = MIRBuilder.buildConstant(LLT::integer(64), Amount.zext(64));
1677
1678 if (MI.getOpcode() == TargetOpcode::G_FSHR) {
1679 Observer.changingInstr(MI);
1680 MI.getOperand(3).setReg(Cast64.getReg(0));
1681 Observer.changedInstr(MI);
1682 }
1683 // If Opcode is FSHL, remove the FSHL instruction and create a FSHR
1684 // instruction
1685 else if (MI.getOpcode() == TargetOpcode::G_FSHL) {
1686 MIRBuilder.buildInstr(TargetOpcode::G_FSHR, {MI.getOperand(0).getReg()},
1687 {MI.getOperand(1).getReg(), MI.getOperand(2).getReg(),
1688 Cast64.getReg(0)});
1689 MI.eraseFromParent();
1690 }
1691 return true;
1692}
1693
1694bool AArch64LegalizerInfo::legalizeICMP(MachineInstr &MI,
1696 MachineIRBuilder &MIRBuilder) const {
1697 Register DstReg = MI.getOperand(0).getReg();
1698 Register SrcReg1 = MI.getOperand(2).getReg();
1699 Register SrcReg2 = MI.getOperand(3).getReg();
1700 LLT DstTy = MRI.getType(DstReg);
1701 LLT SrcTy = MRI.getType(SrcReg1);
1702
1703 // Check the vector types are legal
1704 if (DstTy.getScalarSizeInBits() != SrcTy.getScalarSizeInBits() ||
1705 DstTy.getNumElements() != SrcTy.getNumElements() ||
1706 (DstTy.getSizeInBits() != 64 && DstTy.getSizeInBits() != 128))
1707 return false;
1708
1709 // Lowers G_ICMP NE => G_ICMP EQ to allow better pattern matching for
1710 // following passes
1711 CmpInst::Predicate Pred = (CmpInst::Predicate)MI.getOperand(1).getPredicate();
1712 if (Pred != CmpInst::ICMP_NE)
1713 return true;
1714 Register CmpReg =
1715 MIRBuilder
1716 .buildICmp(CmpInst::ICMP_EQ, MRI.getType(DstReg), SrcReg1, SrcReg2)
1717 .getReg(0);
1718 MIRBuilder.buildNot(DstReg, CmpReg);
1719
1720 MI.eraseFromParent();
1721 return true;
1722}
1723
1724bool AArch64LegalizerInfo::legalizeRotate(MachineInstr &MI,
1726 LegalizerHelper &Helper) const {
1727 // To allow for imported patterns to match, we ensure that the rotate amount
1728 // is 64b with an extension.
1729 Register AmtReg = MI.getOperand(2).getReg();
1730 LLT AmtTy = MRI.getType(AmtReg);
1731 (void)AmtTy;
1732 assert(AmtTy.isScalar() && "Expected a scalar rotate");
1733 assert(AmtTy.getSizeInBits() < 64 && "Expected this rotate to be legal");
1734 auto NewAmt = Helper.MIRBuilder.buildZExt(LLT::integer(64), AmtReg);
1735 Helper.Observer.changingInstr(MI);
1736 MI.getOperand(2).setReg(NewAmt.getReg(0));
1737 Helper.Observer.changedInstr(MI);
1738 return true;
1739}
1740
1741bool AArch64LegalizerInfo::legalizeSmallCMGlobalValue(
1743 GISelChangeObserver &Observer) const {
1744 assert(MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
1745 // We do this custom legalization to convert G_GLOBAL_VALUE into target ADRP +
1746 // G_ADD_LOW instructions.
1747 // By splitting this here, we can optimize accesses in the small code model by
1748 // folding in the G_ADD_LOW into the load/store offset.
1749 auto &GlobalOp = MI.getOperand(1);
1750 // Don't modify an intrinsic call.
1751 if (GlobalOp.isSymbol())
1752 return true;
1753 const auto* GV = GlobalOp.getGlobal();
1754 if (GV->isThreadLocal())
1755 return true; // Don't want to modify TLS vars.
1756
1757 auto &TM = ST->getTargetLowering()->getTargetMachine();
1758 unsigned OpFlags = ST->ClassifyGlobalReference(GV, TM);
1759
1760 if (OpFlags & AArch64II::MO_GOT)
1761 return true;
1762
1763 auto Offset = GlobalOp.getOffset();
1764 Register DstReg = MI.getOperand(0).getReg();
1765 auto ADRP = MIRBuilder.buildInstr(AArch64::ADRP, {LLT::pointer(0, 64)}, {})
1766 .addGlobalAddress(GV, Offset, OpFlags | AArch64II::MO_PAGE);
1767 // Set the regclass on the dest reg too.
1768 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1769
1770 // MO_TAGGED on the page indicates a tagged address. Set the tag now. We do so
1771 // by creating a MOVK that sets bits 48-63 of the register to (global address
1772 // + 0x100000000 - PC) >> 48. The additional 0x100000000 offset here is to
1773 // prevent an incorrect tag being generated during relocation when the
1774 // global appears before the code section. Without the offset, a global at
1775 // `0x0f00'0000'0000'1000` (i.e. at `0x1000` with tag `0xf`) that's referenced
1776 // by code at `0x2000` would result in `0x0f00'0000'0000'1000 - 0x2000 =
1777 // 0x0eff'ffff'ffff'f000`, meaning the tag would be incorrectly set to `0xe`
1778 // instead of `0xf`.
1779 // This assumes that we're in the small code model so we can assume a binary
1780 // size of <= 4GB, which makes the untagged PC relative offset positive. The
1781 // binary must also be loaded into address range [0, 2^48). Both of these
1782 // properties need to be ensured at runtime when using tagged addresses.
1783 if (OpFlags & AArch64II::MO_TAGGED) {
1784 assert(!Offset &&
1785 "Should not have folded in an offset for a tagged global!");
1786 ADRP = MIRBuilder.buildInstr(AArch64::MOVKXi, {LLT::pointer(0, 64)}, {ADRP})
1787 .addGlobalAddress(GV, 0x100000000,
1789 .addImm(48);
1790 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1791 }
1792
1793 MIRBuilder.buildInstr(AArch64::G_ADD_LOW, {DstReg}, {ADRP})
1794 .addGlobalAddress(GV, Offset,
1796 MI.eraseFromParent();
1797 return true;
1798}
1799
1801 MachineInstr &MI) const {
1802 MachineIRBuilder &MIB = Helper.MIRBuilder;
1803 MachineRegisterInfo &MRI = *MIB.getMRI();
1804
1805 auto LowerUnaryOp = [&MI, &MIB](unsigned Opcode) {
1806 MIB.buildInstr(Opcode, {MI.getOperand(0)}, {MI.getOperand(2)});
1807 MI.eraseFromParent();
1808 return true;
1809 };
1810 auto LowerBinOp = [&MI, &MIB](unsigned Opcode) {
1811 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1812 {MI.getOperand(2), MI.getOperand(3)});
1813 MI.eraseFromParent();
1814 return true;
1815 };
1816 auto LowerTriOp = [&MI, &MIB](unsigned Opcode) {
1817 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1818 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4)});
1819 MI.eraseFromParent();
1820 return true;
1821 };
1822
1823 Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
1824 switch (IntrinsicID) {
1825 case Intrinsic::vacopy: {
1826 unsigned PtrSize = ST->isTargetILP32() ? 4 : 8;
1827 unsigned VaListSize =
1828 (ST->isTargetDarwin() || ST->isTargetWindows())
1829 ? PtrSize
1830 : ST->isTargetILP32() ? 20 : 32;
1831
1832 MachineFunction &MF = *MI.getMF();
1834 LLT::integer(VaListSize * 8));
1835 MIB.buildLoad(Val, MI.getOperand(2),
1838 VaListSize, Align(PtrSize)));
1839 MIB.buildStore(Val, MI.getOperand(1),
1842 VaListSize, Align(PtrSize)));
1843 MI.eraseFromParent();
1844 return true;
1845 }
1846 case Intrinsic::get_dynamic_area_offset: {
1847 MIB.buildConstant(MI.getOperand(0).getReg(), 0);
1848 MI.eraseFromParent();
1849 return true;
1850 }
1851 case Intrinsic::aarch64_mops_memset_tag: {
1852 assert(MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS);
1853 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
1854 // the instruction).
1855 auto &Value = MI.getOperand(3);
1856 Register ExtValueReg = MIB.buildAnyExt(LLT::integer(64), Value).getReg(0);
1857 Value.setReg(ExtValueReg);
1858 return true;
1859 }
1860 case Intrinsic::aarch64_prefetch: {
1861 auto &AddrVal = MI.getOperand(1);
1862
1863 int64_t IsWrite = MI.getOperand(2).getImm();
1864 int64_t Target = MI.getOperand(3).getImm();
1865 int64_t IsStream = MI.getOperand(4).getImm();
1866 int64_t IsData = MI.getOperand(5).getImm();
1867
1868 unsigned PrfOp = (IsWrite << 4) | // Load/Store bit
1869 (!IsData << 3) | // IsDataCache bit
1870 (Target << 1) | // Cache level bits
1871 (unsigned)IsStream; // Stream bit
1872
1873 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
1874 MI.eraseFromParent();
1875 return true;
1876 }
1877 case Intrinsic::aarch64_range_prefetch: {
1878 auto &AddrVal = MI.getOperand(1);
1879
1880 int64_t IsWrite = MI.getOperand(2).getImm();
1881 int64_t IsStream = MI.getOperand(3).getImm();
1882 unsigned PrfOp = (IsStream << 2) | IsWrite;
1883
1884 MIB.buildInstr(AArch64::G_AARCH64_RANGE_PREFETCH)
1885 .addImm(PrfOp)
1886 .add(AddrVal)
1887 .addUse(MI.getOperand(4).getReg()); // Metadata
1888 MI.eraseFromParent();
1889 return true;
1890 }
1891 case Intrinsic::aarch64_prefetch_ir: {
1892 auto &AddrVal = MI.getOperand(1);
1893 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(24).add(AddrVal);
1894 MI.eraseFromParent();
1895 return true;
1896 }
1897 case Intrinsic::aarch64_neon_uaddv:
1898 case Intrinsic::aarch64_neon_saddv:
1899 case Intrinsic::aarch64_neon_umaxv:
1900 case Intrinsic::aarch64_neon_smaxv:
1901 case Intrinsic::aarch64_neon_uminv:
1902 case Intrinsic::aarch64_neon_sminv: {
1903 bool IsSigned = IntrinsicID == Intrinsic::aarch64_neon_saddv ||
1904 IntrinsicID == Intrinsic::aarch64_neon_smaxv ||
1905 IntrinsicID == Intrinsic::aarch64_neon_sminv;
1906
1907 auto OldDst = MI.getOperand(0).getReg();
1908 auto OldDstTy = MRI.getType(OldDst);
1909 LLT NewDstTy = MRI.getType(MI.getOperand(2).getReg()).getElementType();
1910 if (OldDstTy == NewDstTy)
1911 return true;
1912
1913 auto NewDst = MRI.createGenericVirtualRegister(NewDstTy);
1914
1915 Helper.Observer.changingInstr(MI);
1916 MI.getOperand(0).setReg(NewDst);
1917 Helper.Observer.changedInstr(MI);
1918
1919 MIB.setInsertPt(MIB.getMBB(), ++MIB.getInsertPt());
1920 MIB.buildExtOrTrunc(IsSigned ? TargetOpcode::G_SEXT : TargetOpcode::G_ZEXT,
1921 OldDst, NewDst);
1922
1923 return true;
1924 }
1925 case Intrinsic::aarch64_neon_uaddlp:
1926 case Intrinsic::aarch64_neon_saddlp: {
1927 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlp
1928 ? AArch64::G_UADDLP
1929 : AArch64::G_SADDLP;
1930 MIB.buildInstr(Opc, {MI.getOperand(0)}, {MI.getOperand(2)});
1931 MI.eraseFromParent();
1932
1933 return true;
1934 }
1935 case Intrinsic::aarch64_neon_uaddlv:
1936 case Intrinsic::aarch64_neon_saddlv: {
1937 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlv
1938 ? AArch64::G_UADDLV
1939 : AArch64::G_SADDLV;
1940 Register DstReg = MI.getOperand(0).getReg();
1941 Register SrcReg = MI.getOperand(2).getReg();
1942 LLT DstTy = MRI.getType(DstReg);
1943
1944 LLT MidTy, ExtTy;
1945 if (DstTy.isScalar() && DstTy.getScalarSizeInBits() <= 32) {
1946 ExtTy = LLT::integer(32);
1947 MidTy = LLT::fixed_vector(4, ExtTy);
1948 } else {
1949 ExtTy = LLT::integer(64);
1950 MidTy = LLT::fixed_vector(2, ExtTy);
1951 }
1952
1953 Register MidReg =
1954 MIB.buildInstr(Opc, {MidTy}, {SrcReg})->getOperand(0).getReg();
1955 Register ZeroReg =
1956 MIB.buildConstant(LLT::integer(64), 0)->getOperand(0).getReg();
1957 Register ExtReg = MIB.buildInstr(AArch64::G_EXTRACT_VECTOR_ELT, {ExtTy},
1958 {MidReg, ZeroReg})
1959 .getReg(0);
1960
1961 if (DstTy.getScalarSizeInBits() < 32)
1962 MIB.buildTrunc(DstReg, ExtReg);
1963 else
1964 MIB.buildCopy(DstReg, ExtReg);
1965
1966 MI.eraseFromParent();
1967
1968 return true;
1969 }
1970 case Intrinsic::aarch64_neon_smax:
1971 return LowerBinOp(TargetOpcode::G_SMAX);
1972 case Intrinsic::aarch64_neon_smin:
1973 return LowerBinOp(TargetOpcode::G_SMIN);
1974 case Intrinsic::aarch64_neon_umax:
1975 return LowerBinOp(TargetOpcode::G_UMAX);
1976 case Intrinsic::aarch64_neon_umin:
1977 return LowerBinOp(TargetOpcode::G_UMIN);
1978 case Intrinsic::aarch64_neon_fmax:
1979 return LowerBinOp(TargetOpcode::G_FMAXIMUM);
1980 case Intrinsic::aarch64_neon_fmin:
1981 return LowerBinOp(TargetOpcode::G_FMINIMUM);
1982 case Intrinsic::aarch64_neon_fmaxnm:
1983 return LowerBinOp(TargetOpcode::G_FMAXNUM);
1984 case Intrinsic::aarch64_neon_fminnm:
1985 return LowerBinOp(TargetOpcode::G_FMINNUM);
1986 case Intrinsic::aarch64_neon_pmul:
1987 return LowerBinOp(TargetOpcode::G_CLMUL);
1988 case Intrinsic::aarch64_neon_pmull:
1989 case Intrinsic::aarch64_neon_pmull64:
1990 return LowerBinOp(AArch64::G_PMULL);
1991 case Intrinsic::aarch64_neon_smull:
1992 return LowerBinOp(AArch64::G_SMULL);
1993 case Intrinsic::aarch64_neon_umull:
1994 return LowerBinOp(AArch64::G_UMULL);
1995 case Intrinsic::aarch64_neon_sabd:
1996 return LowerBinOp(TargetOpcode::G_ABDS);
1997 case Intrinsic::aarch64_neon_uabd:
1998 return LowerBinOp(TargetOpcode::G_ABDU);
1999 case Intrinsic::aarch64_neon_uhadd:
2000 return LowerBinOp(TargetOpcode::G_UAVGFLOOR);
2001 case Intrinsic::aarch64_neon_urhadd:
2002 return LowerBinOp(TargetOpcode::G_UAVGCEIL);
2003 case Intrinsic::aarch64_neon_shadd:
2004 return LowerBinOp(TargetOpcode::G_SAVGFLOOR);
2005 case Intrinsic::aarch64_neon_srhadd:
2006 return LowerBinOp(TargetOpcode::G_SAVGCEIL);
2007 case Intrinsic::aarch64_neon_sqshrn: {
2008 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2009 return true;
2010 // Create right shift instruction. Store the output register in Shr.
2011 auto Shr = MIB.buildInstr(AArch64::G_VASHR,
2012 {MRI.getType(MI.getOperand(2).getReg())},
2013 {MI.getOperand(2), MI.getOperand(3).getImm()});
2014 // Build the narrow intrinsic, taking in Shr.
2015 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_S, {MI.getOperand(0)}, {Shr});
2016 MI.eraseFromParent();
2017 return true;
2018 }
2019 case Intrinsic::aarch64_neon_sqshrun: {
2020 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2021 return true;
2022 // Create right shift instruction. Store the output register in Shr.
2023 auto Shr = MIB.buildInstr(AArch64::G_VASHR,
2024 {MRI.getType(MI.getOperand(2).getReg())},
2025 {MI.getOperand(2), MI.getOperand(3).getImm()});
2026 // Build the narrow intrinsic, taking in Shr.
2027 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {MI.getOperand(0)}, {Shr});
2028 MI.eraseFromParent();
2029 return true;
2030 }
2031 case Intrinsic::aarch64_neon_sqrshrn: {
2032 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2033 return true;
2034 // Create right shift instruction. Store the output register in Shr.
2035 auto Shr = MIB.buildInstr(AArch64::G_SRSHR_I,
2036 {MRI.getType(MI.getOperand(2).getReg())},
2037 {MI.getOperand(2), MI.getOperand(3).getImm()});
2038 // Build the narrow intrinsic, taking in Shr.
2039 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_S, {MI.getOperand(0)}, {Shr});
2040 MI.eraseFromParent();
2041 return true;
2042 }
2043 case Intrinsic::aarch64_neon_sqrshrun: {
2044 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2045 return true;
2046 // Create right shift instruction. Store the output register in Shr.
2047 auto Shr = MIB.buildInstr(AArch64::G_SRSHR_I,
2048 {MRI.getType(MI.getOperand(2).getReg())},
2049 {MI.getOperand(2), MI.getOperand(3).getImm()});
2050 // Build the narrow intrinsic, taking in Shr.
2051 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {MI.getOperand(0)}, {Shr});
2052 MI.eraseFromParent();
2053 return true;
2054 }
2055 case Intrinsic::aarch64_neon_uqrshrn: {
2056 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2057 return true;
2058 // Create right shift instruction. Store the output register in Shr.
2059 auto Shr = MIB.buildInstr(AArch64::G_URSHR_I,
2060 {MRI.getType(MI.getOperand(2).getReg())},
2061 {MI.getOperand(2), MI.getOperand(3).getImm()});
2062 // Build the narrow intrinsic, taking in Shr.
2063 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2064 MI.eraseFromParent();
2065 return true;
2066 }
2067 case Intrinsic::aarch64_neon_uqshrn: {
2068 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2069 return true;
2070 // Create right shift instruction. Store the output register in Shr.
2071 auto Shr = MIB.buildInstr(AArch64::G_VLSHR,
2072 {MRI.getType(MI.getOperand(2).getReg())},
2073 {MI.getOperand(2), MI.getOperand(3).getImm()});
2074 // Build the narrow intrinsic, taking in Shr.
2075 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2076 MI.eraseFromParent();
2077 return true;
2078 }
2079 case Intrinsic::aarch64_neon_sqshlu: {
2080 // Check if last operand is constant vector dup
2081 auto ShiftAmount =
2082 isConstantOrConstantSplatVector(MI.getOperand(3).getReg(), MRI);
2083 if (ShiftAmount) {
2084 // If so, create a new intrinsic with the correct shift amount
2085 MIB.buildInstr(AArch64::G_SQSHLU_I, {MI.getOperand(0)},
2086 {MI.getOperand(2)})
2087 .addImm(ShiftAmount->getSExtValue());
2088 MI.eraseFromParent();
2089 return true;
2090 }
2091 return false;
2092 }
2093 case Intrinsic::aarch64_neon_vsli: {
2094 MIB.buildInstr(
2095 AArch64::G_SLI, {MI.getOperand(0)},
2096 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2097 MI.eraseFromParent();
2098 break;
2099 }
2100 case Intrinsic::aarch64_neon_vsri: {
2101 MIB.buildInstr(
2102 AArch64::G_SRI, {MI.getOperand(0)},
2103 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2104 MI.eraseFromParent();
2105 break;
2106 }
2107 case Intrinsic::aarch64_neon_abs: {
2108 // Lower the intrinsic to G_ABS.
2109 MIB.buildInstr(TargetOpcode::G_ABS, {MI.getOperand(0)}, {MI.getOperand(2)});
2110 MI.eraseFromParent();
2111 return true;
2112 }
2113 case Intrinsic::aarch64_neon_addhn:
2114 return LowerBinOp(AArch64::G_ADDHN);
2115 case Intrinsic::aarch64_neon_sqadd: {
2116 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2117 return LowerBinOp(TargetOpcode::G_SADDSAT);
2118 break;
2119 }
2120 case Intrinsic::aarch64_neon_sqsub: {
2121 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2122 return LowerBinOp(TargetOpcode::G_SSUBSAT);
2123 break;
2124 }
2125 case Intrinsic::aarch64_neon_uqadd: {
2126 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2127 return LowerBinOp(TargetOpcode::G_UADDSAT);
2128 break;
2129 }
2130 case Intrinsic::aarch64_neon_uqsub: {
2131 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2132 return LowerBinOp(TargetOpcode::G_USUBSAT);
2133 break;
2134 }
2135 case Intrinsic::aarch64_neon_udot:
2136 return LowerTriOp(AArch64::G_UDOT);
2137 case Intrinsic::aarch64_neon_sdot:
2138 return LowerTriOp(AArch64::G_SDOT);
2139 case Intrinsic::aarch64_neon_usdot:
2140 return LowerTriOp(AArch64::G_USDOT);
2141 case Intrinsic::aarch64_neon_sqxtn:
2142 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_S);
2143 case Intrinsic::aarch64_neon_sqxtun:
2144 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_U);
2145 case Intrinsic::aarch64_neon_uqxtn:
2146 return LowerUnaryOp(TargetOpcode::G_TRUNC_USAT_U);
2147 case Intrinsic::aarch64_neon_fcvtzu:
2148 return LowerUnaryOp(TargetOpcode::G_FPTOUI_SAT);
2149 case Intrinsic::aarch64_neon_fcvtzs:
2150 return LowerUnaryOp(TargetOpcode::G_FPTOSI_SAT);
2151 case Intrinsic::aarch64_neon_cls:
2152 return LowerUnaryOp(TargetOpcode::G_CTLS);
2153
2154 case Intrinsic::vector_reverse:
2155 // TODO: Add support for vector_reverse
2156 return false;
2157 }
2158
2159 return true;
2160}
2161
2162bool AArch64LegalizerInfo::legalizeShlAshrLshr(
2164 GISelChangeObserver &Observer) const {
2165 assert(MI.getOpcode() == TargetOpcode::G_ASHR ||
2166 MI.getOpcode() == TargetOpcode::G_LSHR ||
2167 MI.getOpcode() == TargetOpcode::G_SHL);
2168 // If the shift amount is a G_CONSTANT, promote it to a 64 bit type so the
2169 // imported patterns can select it later. Either way, it will be legal.
2170 Register AmtReg = MI.getOperand(2).getReg();
2171 LLT AmtRegEltTy = MRI.getType(AmtReg).getScalarType();
2172 auto VRegAndVal = getIConstantVRegValWithLookThrough(AmtReg, MRI);
2173 if (!VRegAndVal)
2174 return true;
2175 // Check the shift amount is in range for an immediate form.
2176 int64_t Amount = VRegAndVal->Value.getSExtValue();
2177 if (Amount > 31)
2178 return true; // This will have to remain a register variant.
2179 auto ExtCst =
2180 MIRBuilder.buildConstant(AmtRegEltTy.changeElementSize(64), Amount);
2181 Observer.changingInstr(MI);
2182 MI.getOperand(2).setReg(ExtCst.getReg(0));
2183 Observer.changedInstr(MI);
2184 return true;
2185}
2186
2188 MachineRegisterInfo &MRI) {
2189 Base = Root;
2190 Offset = 0;
2191
2192 Register NewBase;
2193 int64_t NewOffset;
2194 if (mi_match(Root, MRI, m_GPtrAdd(m_Reg(NewBase), m_ICst(NewOffset))) &&
2195 isShiftedInt<7, 3>(NewOffset)) {
2196 Base = NewBase;
2197 Offset = NewOffset;
2198 }
2199}
2200
2201// FIXME: This should be removed and replaced with the generic bitcast legalize
2202// action.
2203bool AArch64LegalizerInfo::legalizeLoadStore(
2205 GISelChangeObserver &Observer) const {
2206 assert(MI.getOpcode() == TargetOpcode::G_STORE ||
2207 MI.getOpcode() == TargetOpcode::G_LOAD);
2208 // Here we just try to handle vector loads/stores where our value type might
2209 // have pointer elements, which the SelectionDAG importer can't handle. To
2210 // allow the existing patterns for s64 to fire for p0, we just try to bitcast
2211 // the value to use s64 types.
2212
2213 // Custom legalization requires the instruction, if not deleted, must be fully
2214 // legalized. In order to allow further legalization of the inst, we create
2215 // a new instruction and erase the existing one.
2216
2217 Register ValReg = MI.getOperand(0).getReg();
2218 const LLT ValTy = MRI.getType(ValReg);
2219
2220 if (ValTy == LLT::scalar(128)) {
2221
2222 AtomicOrdering Ordering = (*MI.memoperands_begin())->getSuccessOrdering();
2223 bool IsLoad = MI.getOpcode() == TargetOpcode::G_LOAD;
2224 bool IsLoadAcquire = IsLoad && Ordering == AtomicOrdering::Acquire;
2225 bool IsStoreRelease = !IsLoad && Ordering == AtomicOrdering::Release;
2226 bool IsRcpC3 =
2227 ST->hasLSE2() && ST->hasRCPC3() && (IsLoadAcquire || IsStoreRelease);
2228
2229 LLT s64 = LLT::integer(64);
2230
2231 unsigned Opcode;
2232 if (IsRcpC3) {
2233 Opcode = IsLoad ? AArch64::LDIAPPX : AArch64::STILPX;
2234 } else {
2235 // For LSE2, loads/stores should have been converted to monotonic and had
2236 // a fence inserted after them.
2237 assert(Ordering == AtomicOrdering::Monotonic ||
2238 Ordering == AtomicOrdering::Unordered);
2239 assert(ST->hasLSE2() && "ldp/stp not single copy atomic without +lse2");
2240
2241 Opcode = IsLoad ? AArch64::LDPXi : AArch64::STPXi;
2242 }
2243
2244 MachineInstrBuilder NewI;
2245 if (IsLoad) {
2246 NewI = MIRBuilder.buildInstr(Opcode, {s64, s64}, {});
2247 MIRBuilder.buildMergeLikeInstr(
2248 ValReg, {NewI->getOperand(0), NewI->getOperand(1)});
2249 } else {
2250 auto Split = MIRBuilder.buildUnmerge(s64, MI.getOperand(0));
2251 NewI = MIRBuilder.buildInstr(
2252 Opcode, {}, {Split->getOperand(0), Split->getOperand(1)});
2253 }
2254
2255 if (IsRcpC3) {
2256 NewI.addUse(MI.getOperand(1).getReg());
2257 } else {
2258 Register Base;
2259 int Offset;
2260 matchLDPSTPAddrMode(MI.getOperand(1).getReg(), Base, Offset, MRI);
2261 NewI.addUse(Base);
2262 NewI.addImm(Offset / 8);
2263 }
2264
2265 NewI.cloneMemRefs(MI);
2266 constrainSelectedInstRegOperands(*NewI, *ST->getInstrInfo(),
2267 *MRI.getTargetRegisterInfo(),
2268 *ST->getRegBankInfo());
2269 MI.eraseFromParent();
2270 return true;
2271 }
2272
2273 if (!ValTy.isPointerVector() ||
2274 ValTy.getElementType().getAddressSpace() != 0) {
2275 LLVM_DEBUG(dbgs() << "Tried to do custom legalization on wrong load/store");
2276 return false;
2277 }
2278
2279 unsigned PtrSize = ValTy.getElementType().getSizeInBits();
2280 const LLT NewTy = LLT::vector(ValTy.getElementCount(), LLT::integer(PtrSize));
2281 auto &MMO = **MI.memoperands_begin();
2282 MMO.setType(NewTy);
2283
2284 if (MI.getOpcode() == TargetOpcode::G_STORE) {
2285 auto Bitcast = MIRBuilder.buildBitcast(NewTy, ValReg);
2286 MIRBuilder.buildStore(Bitcast.getReg(0), MI.getOperand(1), MMO);
2287 } else {
2288 auto NewLoad = MIRBuilder.buildLoad(NewTy, MI.getOperand(1), MMO);
2289 MIRBuilder.buildBitcast(ValReg, NewLoad);
2290 }
2291 MI.eraseFromParent();
2292 return true;
2293}
2294
2295bool AArch64LegalizerInfo::legalizeVaArg(MachineInstr &MI,
2297 MachineIRBuilder &MIRBuilder) const {
2298 MachineFunction &MF = MIRBuilder.getMF();
2299 Align Alignment(MI.getOperand(2).getImm());
2300 Register Dst = MI.getOperand(0).getReg();
2301 Register ListPtr = MI.getOperand(1).getReg();
2302
2303 LLT PtrTy = MRI.getType(ListPtr);
2304 LLT IntPtrTy = LLT::integer(PtrTy.getSizeInBits());
2305
2306 const unsigned PtrSize = PtrTy.getSizeInBits() / 8;
2307 const Align PtrAlign = Align(PtrSize);
2308 auto List = MIRBuilder.buildLoad(
2309 PtrTy, ListPtr,
2310 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2311 PtrTy, PtrAlign));
2312
2313 MachineInstrBuilder DstPtr;
2314 if (Alignment > PtrAlign) {
2315 // Realign the list to the actual required alignment.
2316 auto AlignMinus1 =
2317 MIRBuilder.buildConstant(IntPtrTy, Alignment.value() - 1);
2318 auto ListTmp = MIRBuilder.buildPtrAdd(PtrTy, List, AlignMinus1.getReg(0));
2319 DstPtr = MIRBuilder.buildMaskLowPtrBits(PtrTy, ListTmp, Log2(Alignment));
2320 } else
2321 DstPtr = List;
2322
2323 LLT ValTy = MRI.getType(Dst);
2324 uint64_t ValSize = ValTy.getSizeInBits() / 8;
2325 MIRBuilder.buildLoad(
2326 Dst, DstPtr,
2327 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2328 ValTy, std::max(Alignment, PtrAlign)));
2329
2330 auto Size = MIRBuilder.buildConstant(IntPtrTy, alignTo(ValSize, PtrAlign));
2331
2332 auto NewList = MIRBuilder.buildPtrAdd(PtrTy, DstPtr, Size.getReg(0));
2333
2334 MIRBuilder.buildStore(NewList, ListPtr,
2335 *MF.getMachineMemOperand(MachinePointerInfo(),
2337 PtrTy, PtrAlign));
2338
2339 MI.eraseFromParent();
2340 return true;
2341}
2342
2343bool AArch64LegalizerInfo::legalizeBitfieldExtract(
2344 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2345 // Only legal if we can select immediate forms.
2346 // TODO: Lower this otherwise.
2347 return getIConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI) &&
2348 getIConstantVRegValWithLookThrough(MI.getOperand(3).getReg(), MRI);
2349}
2350
2351bool AArch64LegalizerInfo::legalizeCTPOP(MachineInstr &MI,
2353 LegalizerHelper &Helper) const {
2354 // When there is no integer popcount instruction (FEAT_CSSC isn't available),
2355 // it can be more efficiently lowered to the following sequence that uses
2356 // AdvSIMD registers/instructions as long as the copies to/from the AdvSIMD
2357 // registers are cheap.
2358 // FMOV D0, X0 // copy 64-bit int to vector, high bits zero'd
2359 // CNT V0.8B, V0.8B // 8xbyte pop-counts
2360 // ADDV B0, V0.8B // sum 8xbyte pop-counts
2361 // UMOV X0, V0.B[0] // copy byte result back to integer reg
2362 //
2363 // For 128 bit vector popcounts, we lower to the following sequence:
2364 // cnt.16b v0, v0 // v8s16, v4s32, v2s64
2365 // uaddlp.8h v0, v0 // v8s16, v4s32, v2s64
2366 // uaddlp.4s v0, v0 // v4s32, v2s64
2367 // uaddlp.2d v0, v0 // v2s64
2368 //
2369 // For 64 bit vector popcounts, we lower to the following sequence:
2370 // cnt.8b v0, v0 // v4s16, v2s32
2371 // uaddlp.4h v0, v0 // v4s16, v2s32
2372 // uaddlp.2s v0, v0 // v2s32
2373
2374 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2375 Register Dst = MI.getOperand(0).getReg();
2376 Register Val = MI.getOperand(1).getReg();
2377 LLT Ty = MRI.getType(Val);
2378
2379 LLT i64 = LLT::integer(64);
2380 LLT i32 = LLT::integer(32);
2381 LLT i16 = LLT::integer(16);
2382 LLT i8 = LLT::integer(8);
2383 unsigned Size = Ty.getSizeInBits();
2384
2385 assert(Ty == MRI.getType(Dst) &&
2386 "Expected src and dst to have the same type!");
2387
2388 if (ST->hasCSSC() && Ty.isScalar() && Size == 128) {
2389
2390 auto Split = MIRBuilder.buildUnmerge(i64, Val);
2391 auto CTPOP1 = MIRBuilder.buildCTPOP(i64, Split->getOperand(0));
2392 auto CTPOP2 = MIRBuilder.buildCTPOP(i64, Split->getOperand(1));
2393 auto Add = MIRBuilder.buildAdd(i64, CTPOP1, CTPOP2);
2394
2395 MIRBuilder.buildZExt(Dst, Add);
2396 MI.eraseFromParent();
2397 return true;
2398 }
2399
2400 if (!ST->hasNEON() ||
2401 MI.getMF()->getFunction().hasFnAttribute(Attribute::NoImplicitFloat)) {
2402 // Use generic lowering when custom lowering is not possible.
2403 return Ty.isScalar() && (Size == 32 || Size == 64) &&
2404 Helper.lowerBitCount(MI) ==
2406 }
2407
2408 // Pre-conditioning: widen Val up to the nearest vector type.
2409 // s32,s64,v4s16,v2s32 -> v8i8
2410 // v8s16,v4s32,v2s64 -> v16i8
2411 LLT VTy = Size == 128 ? LLT::fixed_vector(16, i8) : LLT::fixed_vector(8, i8);
2412 if (Ty.isScalar()) {
2413 assert((Size == 32 || Size == 64 || Size == 128) && "Expected only 32, 64, or 128 bit scalars!");
2414 if (Size == 32) {
2415 Val = MIRBuilder.buildZExt(i64, Val).getReg(0);
2416 }
2417 }
2418 Val = MIRBuilder.buildBitcast(VTy, Val).getReg(0);
2419
2420 // Count bits in each byte-sized lane.
2421 auto CTPOP = MIRBuilder.buildCTPOP(VTy, Val);
2422
2423 // Sum across lanes.
2424 if (ST->hasDotProd() && Ty.isVector() && Ty.getNumElements() >= 2 &&
2425 Ty.getScalarSizeInBits() != 16) {
2426 LLT Dt = Ty == LLT::fixed_vector(2, i64) ? LLT::fixed_vector(4, i32) : Ty;
2427 auto Zeros = MIRBuilder.buildConstant(Dt, 0);
2428 auto Ones = MIRBuilder.buildConstant(VTy, 1);
2429 MachineInstrBuilder Sum;
2430
2431 if (Ty == LLT::fixed_vector(2, i64)) {
2432 auto UDOT =
2433 MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2434 Sum = MIRBuilder.buildInstr(AArch64::G_UADDLP, {Ty}, {UDOT});
2435 } else if (Ty == LLT::fixed_vector(4, i32)) {
2436 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2437 } else if (Ty == LLT::fixed_vector(2, i32)) {
2438 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2439 } else {
2440 llvm_unreachable("unexpected vector shape");
2441 }
2442
2443 Sum->getOperand(0).setReg(Dst);
2444 MI.eraseFromParent();
2445 return true;
2446 }
2447
2448 Register HSum = CTPOP.getReg(0);
2449 unsigned Opc;
2450 SmallVector<LLT> HAddTys;
2451 if (Ty.isScalar()) {
2452 Opc = Intrinsic::aarch64_neon_uaddlv;
2453 HAddTys.push_back(i32);
2454 } else if (Ty == LLT::fixed_vector(8, i16)) {
2455 Opc = Intrinsic::aarch64_neon_uaddlp;
2456 HAddTys.push_back(LLT::fixed_vector(8, i16));
2457 } else if (Ty == LLT::fixed_vector(4, i32)) {
2458 Opc = Intrinsic::aarch64_neon_uaddlp;
2459 HAddTys.push_back(LLT::fixed_vector(8, i16));
2460 HAddTys.push_back(LLT::fixed_vector(4, i32));
2461 } else if (Ty == LLT::fixed_vector(2, i64)) {
2462 Opc = Intrinsic::aarch64_neon_uaddlp;
2463 HAddTys.push_back(LLT::fixed_vector(8, i16));
2464 HAddTys.push_back(LLT::fixed_vector(4, i32));
2465 HAddTys.push_back(LLT::fixed_vector(2, i64));
2466 } else if (Ty == LLT::fixed_vector(4, i16)) {
2467 Opc = Intrinsic::aarch64_neon_uaddlp;
2468 HAddTys.push_back(LLT::fixed_vector(4, i16));
2469 } else if (Ty == LLT::fixed_vector(2, i32)) {
2470 Opc = Intrinsic::aarch64_neon_uaddlp;
2471 HAddTys.push_back(LLT::fixed_vector(4, i16));
2472 HAddTys.push_back(LLT::fixed_vector(2, i32));
2473 } else
2474 llvm_unreachable("unexpected vector shape");
2476 for (LLT HTy : HAddTys) {
2477 UADD = MIRBuilder.buildIntrinsic(Opc, {HTy}).addUse(HSum);
2478 HSum = UADD.getReg(0);
2479 }
2480
2481 // Post-conditioning.
2482 if (Ty.isScalar() && (Size == 64 || Size == 128))
2483 MIRBuilder.buildZExt(Dst, UADD);
2484 else
2485 UADD->getOperand(0).setReg(Dst);
2486 MI.eraseFromParent();
2487 return true;
2488}
2489
2490bool AArch64LegalizerInfo::legalizeAtomicCmpxchg128(
2491 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2492 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2493 LLT i64 = LLT::integer(64);
2494 auto Addr = MI.getOperand(1).getReg();
2495 auto DesiredI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(2));
2496 auto NewI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(3));
2497 auto DstLo = MRI.createGenericVirtualRegister(i64);
2498 auto DstHi = MRI.createGenericVirtualRegister(i64);
2499
2500 MachineInstrBuilder CAS;
2501 if (ST->hasLSE()) {
2502 // We have 128-bit CASP instructions taking XSeqPair registers, which are
2503 // s128. We need the merge/unmerge to bracket the expansion and pair up with
2504 // the rest of the MIR so we must reassemble the extracted registers into a
2505 // 128-bit known-regclass one with code like this:
2506 //
2507 // %in1 = REG_SEQUENCE Lo, Hi ; One for each input
2508 // %out = CASP %in1, ...
2509 // %OldLo = G_EXTRACT %out, 0
2510 // %OldHi = G_EXTRACT %out, 64
2511 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2512 unsigned Opcode;
2513 switch (Ordering) {
2515 Opcode = AArch64::CASPAX;
2516 break;
2518 Opcode = AArch64::CASPLX;
2519 break;
2522 Opcode = AArch64::CASPALX;
2523 break;
2524 default:
2525 Opcode = AArch64::CASPX;
2526 break;
2527 }
2528
2529 LLT s128 = LLT::integer(128);
2530 auto CASDst = MRI.createGenericVirtualRegister(s128);
2531 auto CASDesired = MRI.createGenericVirtualRegister(s128);
2532 auto CASNew = MRI.createGenericVirtualRegister(s128);
2533 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASDesired}, {})
2534 .addUse(DesiredI->getOperand(0).getReg())
2535 .addImm(AArch64::sube64)
2536 .addUse(DesiredI->getOperand(1).getReg())
2537 .addImm(AArch64::subo64);
2538 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASNew}, {})
2539 .addUse(NewI->getOperand(0).getReg())
2540 .addImm(AArch64::sube64)
2541 .addUse(NewI->getOperand(1).getReg())
2542 .addImm(AArch64::subo64);
2543
2544 CAS = MIRBuilder.buildInstr(Opcode, {CASDst}, {CASDesired, CASNew, Addr});
2545
2546 MIRBuilder.buildExtract({DstLo}, {CASDst}, 0);
2547 MIRBuilder.buildExtract({DstHi}, {CASDst}, 64);
2548 } else {
2549 // The -O0 CMP_SWAP_128 is friendlier to generate code for because LDXP/STXP
2550 // can take arbitrary registers so it just has the normal GPR64 operands the
2551 // rest of AArch64 is expecting.
2552 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2553 unsigned Opcode;
2554 switch (Ordering) {
2556 Opcode = AArch64::CMP_SWAP_128_ACQUIRE;
2557 break;
2559 Opcode = AArch64::CMP_SWAP_128_RELEASE;
2560 break;
2563 Opcode = AArch64::CMP_SWAP_128;
2564 break;
2565 default:
2566 Opcode = AArch64::CMP_SWAP_128_MONOTONIC;
2567 break;
2568 }
2569
2570 auto Scratch = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
2571 CAS = MIRBuilder.buildInstr(Opcode, {DstLo, DstHi, Scratch},
2572 {Addr, DesiredI->getOperand(0),
2573 DesiredI->getOperand(1), NewI->getOperand(0),
2574 NewI->getOperand(1)});
2575 }
2576
2577 CAS.cloneMemRefs(MI);
2578 constrainSelectedInstRegOperands(*CAS, *ST->getInstrInfo(),
2579 *MRI.getTargetRegisterInfo(),
2580 *ST->getRegBankInfo());
2581
2582 MIRBuilder.buildMergeLikeInstr(MI.getOperand(0), {DstLo, DstHi});
2583 MI.eraseFromParent();
2584 return true;
2585}
2586
2587bool AArch64LegalizerInfo::legalizeCTTZ(MachineInstr &MI,
2588 LegalizerHelper &Helper) const {
2589 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2590 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2591 LLT Ty = MRI.getType(MI.getOperand(1).getReg());
2592 auto BitReverse = MIRBuilder.buildBitReverse(Ty, MI.getOperand(1));
2593 MIRBuilder.buildCTLZ(MI.getOperand(0).getReg(), BitReverse);
2594 MI.eraseFromParent();
2595 return true;
2596}
2597
2598bool AArch64LegalizerInfo::legalizeMemOps(MachineInstr &MI,
2599 LegalizerHelper &Helper) const {
2600 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2601
2602 // Tagged version MOPSMemorySetTagged is legalised in legalizeIntrinsic
2603 if (MI.getOpcode() == TargetOpcode::G_MEMSET ||
2604 MI.getOpcode() == TargetOpcode::G_MEMSET_INLINE) {
2605 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
2606 // the instruction).
2607 auto &Value = MI.getOperand(1);
2608 Register ExtValueReg =
2609 MIRBuilder.buildAnyExt(LLT::integer(64), Value).getReg(0);
2610 Value.setReg(ExtValueReg);
2611 return true;
2612 }
2613
2614 return false;
2615}
2616
2617bool AArch64LegalizerInfo::legalizeExtractVectorElt(
2618 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2619 const GExtractVectorElement *Element = cast<GExtractVectorElement>(&MI);
2620 auto VRegAndVal =
2622 if (VRegAndVal)
2623 return true;
2624 LLT VecTy = MRI.getType(Element->getVectorReg());
2625 if (VecTy.isScalableVector())
2626 return true;
2627 return Helper.lowerExtractInsertVectorElt(MI) !=
2629}
2630
2631bool AArch64LegalizerInfo::legalizeDynStackAlloc(
2632 MachineInstr &MI, LegalizerHelper &Helper) const {
2633 MachineFunction &MF = *MI.getParent()->getParent();
2634 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2635 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2636
2637 // If stack probing is not enabled for this function, use the default
2638 // lowering.
2639 if (!MF.getFunction().hasFnAttribute("probe-stack") ||
2640 MF.getFunction().getFnAttribute("probe-stack").getValueAsString() !=
2641 "inline-asm") {
2642 Helper.lowerDynStackAlloc(MI);
2643 return true;
2644 }
2645
2646 Register Dst = MI.getOperand(0).getReg();
2647 Register AllocSize = MI.getOperand(1).getReg();
2648 Align Alignment = assumeAligned(MI.getOperand(2).getImm());
2649
2650 assert(MRI.getType(Dst) == LLT::pointer(0, 64) &&
2651 "Unexpected type for dynamic alloca");
2652 assert(MRI.getType(AllocSize) == LLT::scalar(64) &&
2653 "Unexpected type for dynamic alloca");
2654
2655 LLT PtrTy = MRI.getType(Dst);
2656 Register SPReg =
2658 Register SPTmp =
2659 Helper.getDynStackAllocTargetPtr(SPReg, AllocSize, Alignment, PtrTy);
2660 auto NewMI =
2661 MIRBuilder.buildInstr(AArch64::PROBED_STACKALLOC_DYN, {}, {SPTmp});
2662 MRI.setRegClass(NewMI.getReg(0), &AArch64::GPR64commonRegClass);
2663 MIRBuilder.setInsertPt(*NewMI->getParent(), NewMI);
2664 MIRBuilder.buildCopy(Dst, SPTmp);
2665
2666 MI.eraseFromParent();
2667 return true;
2668}
2669
2670bool AArch64LegalizerInfo::legalizePrefetch(MachineInstr &MI,
2671 LegalizerHelper &Helper) const {
2672 MachineIRBuilder &MIB = Helper.MIRBuilder;
2673 auto &AddrVal = MI.getOperand(0);
2674
2675 int64_t IsWrite = MI.getOperand(1).getImm();
2676 int64_t Locality = MI.getOperand(2).getImm();
2677 int64_t IsData = MI.getOperand(3).getImm();
2678
2679 bool IsStream = Locality == 0;
2680 if (Locality != 0) {
2681 assert(Locality <= 3 && "Prefetch locality out-of-range");
2682 // The locality degree is the opposite of the cache speed.
2683 // Put the number the other way around.
2684 // The encoding starts at 0 for level 1
2685 Locality = 3 - Locality;
2686 }
2687
2688 unsigned PrfOp = (IsWrite << 4) | (!IsData << 3) | (Locality << 1) | IsStream;
2689
2690 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
2691 MI.eraseFromParent();
2692 return true;
2693}
2694
2695bool AArch64LegalizerInfo::legalizeConcatVectors(
2697 MachineIRBuilder &MIRBuilder) const {
2698 // Widen sub-byte element vectors to byte-sized elements before concatenating.
2699 // This is analogous to SDAG's integer type promotion for sub-byte types.
2701 Register DstReg = Concat.getReg(0);
2702 LLT DstTy = MRI.getType(DstReg);
2703 assert(DstTy.getScalarSizeInBits() < 8 && "Expected dst ty to be < 8b");
2704
2705 unsigned WideEltSize =
2706 std::max(8u, (unsigned)PowerOf2Ceil(DstTy.getScalarSizeInBits()));
2707 LLT SrcTy = MRI.getType(Concat.getSourceReg(0));
2708 LLT WideSrcTy = SrcTy.changeElementSize(WideEltSize);
2709 LLT WideDstTy = DstTy.changeElementSize(WideEltSize);
2710
2711 SmallVector<Register> WideSrcs;
2712 for (unsigned I = 0; I < Concat.getNumSources(); ++I) {
2713 auto Wide = MIRBuilder.buildAnyExt(WideSrcTy, Concat.getSourceReg(I));
2714 WideSrcs.push_back(Wide.getReg(0));
2715 }
2716
2717 auto WideConcat = MIRBuilder.buildConcatVectors(WideDstTy, WideSrcs);
2718 MIRBuilder.buildTrunc(DstReg, WideConcat);
2719 MI.eraseFromParent();
2720 return true;
2721}
2722
2723bool AArch64LegalizerInfo::legalizeFptrunc(MachineInstr &MI,
2724 MachineIRBuilder &MIRBuilder,
2725 MachineRegisterInfo &MRI) const {
2726 auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
2727
2728 // This function legalizes f64 -> bf16 and f64 -> f16 truncations via f64 ->
2729 // f32 G_FPTRUNC_ODD and f32 -> [b]f16 G_FPTRUNC, which apparently avoids the
2730 // usual double-rounding issue that could be present from using twin
2731 // G_FPTRUNC.
2732
2733 if (DstTy.isBFloat16() && SrcTy.isFloat64()) {
2734 auto Mid = MIRBuilder.buildInstr(AArch64::G_FPTRUNC_ODD, {LLT::float32()},
2735 {Src}, MI.getFlags());
2736 MIRBuilder.buildInstr(AArch64::G_FPTRUNC, {Dst}, {Mid}, MI.getFlags());
2737 MI.eraseFromParent();
2738 return true;
2739 }
2740
2741 assert(SrcTy.isFixedVector() && isPowerOf2_32(SrcTy.getNumElements()) &&
2742 "Expected a power of 2 elements");
2743
2744 // We must mutate types here as FPTrunc may be used on a IEEE floating point
2745 // or a brainfloat.
2746 LLT v2s16 = DstTy.changeElementCount(2);
2747 LLT v4s16 = DstTy.changeElementCount(4);
2748 LLT v2s32 = SrcTy.changeElementCount(2).changeElementSize(32);
2749 LLT v4s32 = SrcTy.changeElementCount(4).changeElementSize(32);
2750 LLT v2s64 = SrcTy.changeElementCount(2);
2751
2752 SmallVector<Register> RegsToUnmergeTo;
2753 SmallVector<Register> TruncOddDstRegs;
2754 SmallVector<Register> RegsToMerge;
2755
2756 unsigned ElemCount = SrcTy.getNumElements();
2757
2758 // Find the biggest size chunks we can work with
2759 int StepSize = ElemCount % 4 ? 2 : 4;
2760
2761 // If we have a power of 2 greater than 2, we need to first unmerge into
2762 // enough pieces
2763 if (ElemCount <= 2)
2764 RegsToUnmergeTo.push_back(Src);
2765 else {
2766 for (unsigned i = 0; i < ElemCount / 2; ++i)
2767 RegsToUnmergeTo.push_back(MRI.createGenericVirtualRegister(v2s64));
2768
2769 MIRBuilder.buildUnmerge(RegsToUnmergeTo, Src);
2770 }
2771
2772 // Create all of the round-to-odd instructions and store them
2773 for (auto SrcReg : RegsToUnmergeTo) {
2774 Register Mid = MIRBuilder
2775 .buildInstr(AArch64::G_FPTRUNC_ODD, {v2s32}, {SrcReg},
2776 MI.getFlags())
2777 .getReg(0);
2778 TruncOddDstRegs.push_back(Mid);
2779 }
2780
2781 // Truncate 4s32 to 4s16 if we can to reduce instruction count, otherwise
2782 // truncate 2s32 to 2s16.
2783 unsigned Index = 0;
2784 for (unsigned LoopIter = 0; LoopIter < ElemCount / StepSize; ++LoopIter) {
2785 if (StepSize == 4) {
2786 Register ConcatDst =
2787 MIRBuilder
2789 {v4s32}, {TruncOddDstRegs[Index++], TruncOddDstRegs[Index++]})
2790 .getReg(0);
2791
2792 RegsToMerge.push_back(
2793 MIRBuilder.buildFPTrunc(v4s16, ConcatDst, MI.getFlags()).getReg(0));
2794 } else {
2795 RegsToMerge.push_back(
2796 MIRBuilder
2797 .buildFPTrunc(v2s16, TruncOddDstRegs[Index++], MI.getFlags())
2798 .getReg(0));
2799 }
2800 }
2801
2802 // If there is only one register, replace the destination
2803 if (RegsToMerge.size() == 1) {
2804 MRI.replaceRegWith(Dst, RegsToMerge.pop_back_val());
2805 MI.eraseFromParent();
2806 return true;
2807 }
2808
2809 // Merge the rest of the instructions & replace the register
2810 Register Fin = MIRBuilder.buildMergeLikeInstr(DstTy, RegsToMerge).getReg(0);
2811 MRI.replaceRegWith(Dst, Fin);
2812 MI.eraseFromParent();
2813 return true;
2814}
2815
2816bool AArch64LegalizerInfo::legalizeGetRounding(MachineInstr &MI,
2817 MachineIRBuilder &MIRBuilder,
2819 LegalizerHelper &Helper) const {
2820 const LLT I32 = LLT::integer(32);
2821 const LLT I64 = LLT::integer(64);
2822
2823 Register Dst = MI.getOperand(0).getReg();
2824 Register FPCR64 = MRI.createGenericVirtualRegister(I64);
2825 MachineInstrBuilder GetFPCR =
2826 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, ArrayRef{FPCR64});
2827
2828 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2829 // 3->0, so we add one to the FPCR bits for the rounding mode.
2830 // Instead of shifting and then adding as `((FPCR >> 22) + 1) & 0b11` which
2831 // generates 3 instructions, we increment the rounding mode with
2832 // `(FPCR + (1 << 22))` and extract the bits. The shift and addition is done
2833 // in one instruction as `add .., .., #1024, lsl #12`, so overall we generate
2834 // one less instruction.
2835 auto FPCR32 = MIRBuilder.buildTrunc(I32, GetFPCR);
2836 auto One = MIRBuilder.buildConstant(I32, 1U << 22);
2837 auto Added = MIRBuilder.buildAdd(I32, FPCR32, One);
2838 auto LSB = MIRBuilder.buildConstant(I32, 22);
2839 auto Width = MIRBuilder.buildConstant(I32, 2);
2840 MIRBuilder.buildInstr(TargetOpcode::G_UBFX, {Dst}, {Added, LSB, Width});
2841
2842 MI.eraseFromParent();
2843 return true;
2844}
2845
2846bool AArch64LegalizerInfo::legalizeSetRounding(MachineInstr &MI,
2847 MachineIRBuilder &MIRBuilder,
2849 LegalizerHelper &Helper) const {
2850 const LLT I32 = LLT::integer(32);
2851 const LLT I64 = LLT::integer(64);
2852
2853 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2854 // 3->0, so calculate the new value of FPCR[23:22] as `((arg - 1) & 3) << 22`.
2855 Register RM = MI.getOperand(0).getReg();
2856 auto One = MIRBuilder.buildConstant(I32, 1);
2857 auto Subtracted = MIRBuilder.buildSub(I32, RM, One);
2858 auto Mask = MIRBuilder.buildConstant(I32, 0b11);
2859 auto Masked = MIRBuilder.buildAnd(I32, Subtracted, Mask);
2860 auto ShiftAmount = MIRBuilder.buildConstant(I32, 22);
2861 auto Shifted = MIRBuilder.buildShl(I32, Masked, ShiftAmount);
2862
2863 // Get current value of FPCR.
2864 MachineInstrBuilder GetFPCR =
2865 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, {I64});
2866
2867 // (FPCR & ~Mask) | Shifted
2868 auto FPCRMask = MIRBuilder.buildConstant(I64, ~((int64_t)0b11 << 22));
2869 auto FPCRMasked = MIRBuilder.buildAnd(I64, GetFPCR, FPCRMask);
2870 auto ShiftedS64 = MIRBuilder.buildZExt(I64, Shifted);
2871 auto FPCRUpdated = MIRBuilder.buildOr(I64, FPCRMasked, ShiftedS64);
2872
2873 // Write new FPCR.
2874 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_set_fpcr, ArrayRef<Register>())
2875 .addUse(FPCRUpdated.getReg(0));
2876
2877 MI.eraseFromParent();
2878
2879 return true;
2880}
static void matchLDPSTPAddrMode(Register Root, Register &Base, int &Offset, MachineRegisterInfo &MRI)
This file declares the targeting of the Machinelegalizer class for AArch64.
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
static Error unsupported(const char *Str, const Triple &T)
Definition MachO.cpp:77
Declares convenience wrapper classes for interpreting MachineInstr instances as specific generic oper...
IRTranslator LLVM IR MI
Interface for Targets to specify which operations they can successfully select and how the others sho...
#define I(x, y, z)
Definition MD5.cpp:57
Contains matchers for matching SSA Machine Instructions.
This file declares the MachineIRBuilder class.
Promote Memory to Register
Definition Mem2Reg.cpp:110
static MCRegister getReg(const MCDisassembler *D, unsigned RC, unsigned RegNo)
ppc ctr loops verify
if(PassOpts->AAPipeline)
static constexpr MCPhysReg SPReg
This file contains some templates that are useful if you are working with the STL at all.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static constexpr int Concat[]
bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, LostDebugLocObserver &LocObserver) const override
Called for instructions with the Custom LegalizationAction.
bool legalizeIntrinsic(LegalizerHelper &Helper, MachineInstr &MI) const override
AArch64LegalizerInfo(const AArch64Subtarget &ST)
Class for arbitrary precision integers.
Definition APInt.h:78
LLVM_ABI APInt zext(unsigned width) const
Zero extend to a new width.
Definition APInt.cpp:1056
LLVM_ABI APInt urem(const APInt &RHS) const
Unsigned remainder operation.
Definition APInt.cpp:1693
int64_t getSExtValue() const
Get sign extended value.
Definition APInt.h:1583
LLVM_ABI StringRef getValueAsString() const
Return the attribute's value as a string.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ ICMP_NE
not equal
Definition InstrTypes.h:762
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:762
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition Function.cpp:727
Abstract class that contains various methods for clients to notify about changes.
virtual void changingInstr(MachineInstr &MI)=0
This instruction is about to be mutated in some way.
virtual void changedInstr(MachineInstr &MI)=0
This instruction was mutated in some way.
static constexpr LLT float64()
Get a 64-bit IEEE double value.
LLT changeElementCount(ElementCount EC) const
Return a vector or scalar with the same element type and the new element count.
constexpr bool isScalableVector() const
Returns true if the LLT is a scalable vector.
constexpr unsigned getScalarSizeInBits() const
constexpr bool isScalar() const
static constexpr LLT scalable_vector(unsigned MinNumElements, unsigned ScalarSizeInBits)
Get a low-level scalable vector of some number of elements and element width.
static constexpr LLT vector(ElementCount EC, unsigned ScalarSizeInBits)
Get a low-level vector of some number of elements and element width.
LLT getScalarType() const
constexpr bool isPointerVector() const
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
constexpr uint16_t getNumElements() const
Returns the number of elements in a vector LLT.
static constexpr LLT float128()
Get a 128-bit IEEE quad value.
constexpr bool isVector() const
static constexpr LLT pointer(unsigned AddressSpace, unsigned SizeInBits)
Get a low-level pointer in the given address space.
constexpr TypeSize getSizeInBits() const
Returns the total size of the type. Must only be called on sized types.
constexpr ElementCount getElementCount() const
LLT divide(int Factor) const
Return a type that is Factor times smaller.
static constexpr LLT float16()
Get a 16-bit IEEE half value.
constexpr unsigned getAddressSpace() const
static constexpr LLT fixed_vector(unsigned NumElements, unsigned ScalarSizeInBits)
Get a low-level fixed-width vector of some number of elements and element width.
constexpr bool isFixedVector() const
Returns true if the LLT is a fixed vector.
static LLT integer(unsigned SizeInBits)
static constexpr LLT bfloat16()
LLT getElementType() const
Returns the vector's element type. Only valid for vector types.
static constexpr LLT float32()
Get a 32-bit IEEE float value.
bool isFloat64() const
LLT changeElementSize(unsigned NewEltSize) const
If this type is a vector, return a vector with the same number of elements but the new element size.
LegalizeRuleSet & minScalar(unsigned TypeIdx, const LLT Ty)
Ensure the scalar is at least as wide as Ty.
LegalizeRuleSet & widenScalarOrEltToNextPow2OrMinSize(unsigned TypeIdx, unsigned MinSize=0)
Widen the scalar or vector element type to the next power of two that is at least MinSize.
LegalizeRuleSet & legalFor(std::initializer_list< LLT > Types)
The instruction is legal when type index 0 is any type in the given list.
LegalizeRuleSet & maxScalarEltSameAsIf(LegalityPredicate Predicate, unsigned TypeIdx, unsigned SmallTypeIdx)
Conditionally narrow the scalar or elt to match the size of another.
LegalizeRuleSet & unsupported()
The instruction is unsupported.
LegalizeRuleSet & scalarSameSizeAs(unsigned TypeIdx, unsigned SameSizeIdx)
Change the type TypeIdx to have the same scalar size as type SameSizeIdx.
LegalizeRuleSet & bitcastIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
The specified type index is coerced if predicate is true.
LegalizeRuleSet & libcallFor(std::initializer_list< LLT > Types)
LegalizeRuleSet & minScalarOrElt(unsigned TypeIdx, const LLT Ty)
Ensure the scalar or element is at least as wide as Ty.
LegalizeRuleSet & clampMaxNumElements(unsigned TypeIdx, const LLT EltTy, unsigned MaxElements)
Limit the number of elements in EltTy vectors to at most MaxElements.
LegalizeRuleSet & clampMinNumElements(unsigned TypeIdx, const LLT EltTy, unsigned MinElements)
Limit the number of elements in EltTy vectors to at least MinElements.
LegalizeRuleSet & widenVectorEltsToVectorMinSize(unsigned TypeIdx, unsigned VectorSize)
Ensure the vector size is at least as wide as VectorSize by promoting the element.
LegalizeRuleSet & lowerIfMemSizeNotPow2()
Lower a memory operation if the memory size, rounded to bytes, is not a power of 2.
LegalizeRuleSet & minScalarEltSameAsIf(LegalityPredicate Predicate, unsigned TypeIdx, unsigned LargeTypeIdx)
Conditionally widen the scalar or elt to match the size of another.
LegalizeRuleSet & customForCartesianProduct(std::initializer_list< LLT > Types)
LegalizeRuleSet & lowerIfMemSizeNotByteSizePow2()
Lower a memory operation if the memory access size is not a round power of 2 byte size.
LegalizeRuleSet & moreElementsToNextPow2(unsigned TypeIdx)
Add more elements to the vector to reach the next power of two.
LegalizeRuleSet & narrowScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
Narrow the scalar to the one selected by the mutation if the predicate is true.
LegalizeRuleSet & lower()
The instruction is lowered.
LegalizeRuleSet & moreElementsIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
Add more elements to reach the type selected by the mutation if the predicate is true.
LegalizeRuleSet & lowerFor(std::initializer_list< LLT > Types)
The instruction is lowered when type index 0 is any type in the given list.
LegalizeRuleSet & scalarizeIf(LegalityPredicate Predicate, unsigned TypeIdx)
LegalizeRuleSet & lowerIf(LegalityPredicate Predicate)
The instruction is lowered if predicate is true.
LegalizeRuleSet & clampScalar(unsigned TypeIdx, const LLT MinTy, const LLT MaxTy)
Limit the range of scalar sizes to MinTy and MaxTy.
LegalizeRuleSet & custom()
Unconditionally custom lower.
LegalizeRuleSet & minScalarSameAs(unsigned TypeIdx, unsigned LargeTypeIdx)
Widen the scalar to match the size of another.
LegalizeRuleSet & unsupportedIf(LegalityPredicate Predicate)
LegalizeRuleSet & minScalarOrEltIf(LegalityPredicate Predicate, unsigned TypeIdx, const LLT Ty)
Ensure the scalar or element is at least as wide as Ty.
LegalizeRuleSet & widenScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
Widen the scalar to the one selected by the mutation if the predicate is true.
LegalizeRuleSet & alwaysLegal()
LegalizeRuleSet & clampNumElements(unsigned TypeIdx, const LLT MinTy, const LLT MaxTy)
Limit the number of elements for the given vectors to at least MinTy's number of elements and at most...
LegalizeRuleSet & maxScalarIf(LegalityPredicate Predicate, unsigned TypeIdx, const LLT Ty)
Conditionally limit the maximum size of the scalar.
LegalizeRuleSet & customIf(LegalityPredicate Predicate)
LegalizeRuleSet & widenScalarToNextPow2(unsigned TypeIdx, unsigned MinSize=0)
Widen the scalar to the next power of two that is at least MinSize.
LegalizeRuleSet & scalarize(unsigned TypeIdx)
LegalizeRuleSet & legalForCartesianProduct(std::initializer_list< LLT > Types)
The instruction is legal when type indexes 0 and 1 are both in the given list.
LegalizeRuleSet & legalForTypesWithMemDesc(std::initializer_list< LegalityPredicates::TypePairAndMemDesc > TypesAndMemDesc)
The instruction is legal when type indexes 0 and 1 along with the memory size and minimum alignment i...
LegalizeRuleSet & legalIf(LegalityPredicate Predicate)
The instruction is legal if predicate is true.
LLVM_ABI LegalizeResult lowerDynStackAlloc(MachineInstr &MI)
LLVM_ABI LegalizeResult lowerBitCount(MachineInstr &MI)
LLVM_ABI LegalizeResult lowerExtractInsertVectorElt(MachineInstr &MI)
Lower a vector extract or insert by writing the vector to a stack temporary and reloading the element...
LLVM_ABI LegalizeResult lowerAbsToCNeg(MachineInstr &MI)
const TargetLowering & getTargetLowering() const
LLVM_ABI LegalizeResult lowerFunnelShiftAsShifts(MachineInstr &MI)
LLVM_ABI MachineInstrBuilder createStackStoreLoad(const DstOp &Res, const SrcOp &Val)
Create a store of Val to a stack temporary and return a load as the same type as Res.
@ Legalized
Instruction has been legalized and the MachineFunction changed.
@ UnableToLegalize
Some kind of error has occurred and we could not legalize this instruction.
GISelChangeObserver & Observer
To keep track of changes made by the LegalizerHelper.
LLVM_ABI Register getDynStackAllocTargetPtr(Register SPReg, Register AllocSize, Align Alignment, LLT PtrTy)
MachineIRBuilder & MIRBuilder
Expose MIRBuilder so clients can set their own RecordInsertInstruction functions.
LegalizeRuleSet & getActionDefinitionsBuilder(unsigned Opcode)
Get the action definition builder for the given opcode.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
MachineMemOperand * getMachineMemOperand(MachinePointerInfo PtrInfo, MachineMemOperand::Flags F, LLT MemTy, Align BaseAlignment, const MMOMetadata &Metadata=MMOMetadata(), SyncScope::ID SSID=SyncScope::System, AtomicOrdering Ordering=AtomicOrdering::NotAtomic, AtomicOrdering FailureOrdering=AtomicOrdering::NotAtomic)
getMachineMemOperand - Allocate a new MachineMemOperand.
Helper class to build MachineInstr.
void setInsertPt(MachineBasicBlock &MBB, MachineBasicBlock::iterator II)
Set the insertion point before the specified position.
MachineInstrBuilder buildAdd(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_ADD Op0, Op1.
MachineInstrBuilder buildNot(const DstOp &Dst, const SrcOp &Src0)
Build and insert a bitwise not, NegOne = G_CONSTANT -1 Res = G_OR Op0, NegOne.
MachineInstrBuilder buildUnmerge(ArrayRef< LLT > Res, const SrcOp &Op)
Build and insert Res0, ... = G_UNMERGE_VALUES Op.
MachineInstrBuilder buildExtract(const DstOp &Res, const SrcOp &Src, uint64_t Index)
Build and insert Res0, ... = G_EXTRACT Src, Idx0.
MachineInstrBuilder buildAnd(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1)
Build and insert Res = G_AND Op0, Op1.
MachineInstrBuilder buildICmp(CmpInst::Predicate Pred, const DstOp &Res, const SrcOp &Op0, const SrcOp &Op1, std::optional< unsigned > Flags=std::nullopt)
Build and insert a Res = G_ICMP Pred, Op0, Op1.
MachineBasicBlock::iterator getInsertPt()
Current insertion point for new instructions.
MachineInstrBuilder buildZExt(const DstOp &Res, const SrcOp &Op, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_ZEXT Op.
MachineInstrBuilder buildConcatVectors(const DstOp &Res, ArrayRef< Register > Ops)
Build and insert Res = G_CONCAT_VECTORS Op0, ...
MachineInstrBuilder buildSub(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_SUB Op0, Op1.
MachineInstrBuilder buildIntrinsic(Intrinsic::ID ID, ArrayRef< Register > Res, bool HasSideEffects, bool isConvergent)
Build and insert a G_INTRINSIC instruction.
MachineInstrBuilder buildCTLZ(const DstOp &Dst, const SrcOp &Src0)
Build and insert Res = G_CTLZ Op0, Src0.
MachineInstrBuilder buildMergeLikeInstr(const DstOp &Res, ArrayRef< Register > Ops)
Build and insert Res = G_MERGE_VALUES Op0, ... or Res = G_BUILD_VECTOR Op0, ... or Res = G_CONCAT_VEC...
MachineInstrBuilder buildLoad(const DstOp &Res, const SrcOp &Addr, MachineMemOperand &MMO)
Build and insert Res = G_LOAD Addr, MMO.
MachineInstrBuilder buildPtrAdd(const DstOp &Res, const SrcOp &Op0, const SrcOp &Op1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_PTR_ADD Op0, Op1.
MachineInstrBuilder buildBitReverse(const DstOp &Dst, const SrcOp &Src)
Build and insert Dst = G_BITREVERSE Src.
MachineInstrBuilder buildShl(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
MachineInstrBuilder buildStore(const SrcOp &Val, const SrcOp &Addr, MachineMemOperand &MMO)
Build and insert G_STORE Val, Addr, MMO.
MachineInstrBuilder buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
MachineInstrBuilder buildCTPOP(const DstOp &Dst, const SrcOp &Src0)
Build and insert Res = G_CTPOP Op0, Src0.
MachineFunction & getMF()
Getter for the function we currently build.
MachineInstrBuilder buildExtOrTrunc(unsigned ExtOpc, const DstOp &Res, const SrcOp &Op)
Build and insert Res = ExtOpc, Res = G_TRUNC Op, or Res = COPY Op depending on the differing sizes of...
MachineInstrBuilder buildTrunc(const DstOp &Res, const SrcOp &Op, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_TRUNC Op.
const MachineBasicBlock & getMBB() const
Getter for the basic block we currently build.
MachineInstrBuilder buildAnyExt(const DstOp &Res, const SrcOp &Op)
Build and insert Res = G_ANYEXT Op0.
MachineInstrBuilder buildBitcast(const DstOp &Dst, const SrcOp &Src)
Build and insert Dst = G_BITCAST Src.
MachineRegisterInfo * getMRI()
Getter for MRI.
MachineInstrBuilder buildFPTrunc(const DstOp &Res, const SrcOp &Op, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_FPTRUNC Op.
MachineInstrBuilder buildOr(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_OR Op0, Op1.
MachineInstrBuilder buildCopy(const DstOp &Res, const SrcOp &Op)
Build and insert Res = COPY Op.
MachineInstrBuilder buildMaskLowPtrBits(const DstOp &Res, const SrcOp &Op0, uint32_t NumBits)
Build and insert Res = G_PTRMASK Op0, G_CONSTANT (1 << NumBits) - 1.
virtual MachineInstrBuilder buildConstant(const DstOp &Res, const ConstantInt &Val)
Build and insert Res = G_CONSTANT Val.
Register getReg(unsigned Idx) const
Get the register for the operand index.
const MachineInstrBuilder & addUse(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register use operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & add(const MachineOperand &MO) const
const MachineInstrBuilder & cloneMemRefs(const MachineInstr &OtherMI) const
Representation of each machine instruction.
const MachineOperand & getOperand(unsigned i) const
@ MOLoad
The memory access reads data.
@ MOStore
The memory access writes data.
LLVM_ABI void setReg(Register Reg)
Change the register this operand corresponds to.
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
LLT getType(Register Reg) const
Get the low-level type of Reg or LLT{} if Reg is not a generic (target independent) virtual register.
LLVM_ABI void setRegClass(Register Reg, const TargetRegisterClass *RC)
setRegClass - Set the register class of the specified virtual register.
LLVM_ABI Register createGenericVirtualRegister(LLT Ty, StringRef Name="")
Create and return a new generic virtual register with low-level type Ty.
const TargetRegisterInfo * getTargetRegisterInfo() const
LLVM_ABI void replaceRegWith(Register FromReg, Register ToReg)
replaceRegWith - Replace all instances of FromReg with ToReg in the machine function.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Register getStackPointerRegisterToSaveRestore() const
If a physical register, this specifies the register that llvm.savestack/llvm.restorestack should save...
Primary interface to the complete machine description for the target machine.
Target - Wrapper for Target specific information.
LLVM Value Representation.
Definition Value.h:75
constexpr LeafTy divideCoefficientBy(ScalarTy RHS) const
We do not provide the '/' operator here because division for polynomial types does not work in the sa...
Definition TypeSize.h:252
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ MO_NC
MO_NC - Indicates whether the linker is expected to check the symbol reference for overflow.
@ MO_PAGEOFF
MO_PAGEOFF - A symbol operand with this flag represents the offset of that symbol within a 4K page.
@ MO_GOT
MO_GOT - This flag indicates that a symbol operand represents the address of the GOT entry for the sy...
@ MO_PREL
MO_PREL - Indicates that the bits of the symbol operand represented by MO_G0 etc are PC relative.
@ MO_PAGE
MO_PAGE - A symbol operand with this flag represents the pc-relative offset of the 4K page containing...
@ MO_TAGGED
MO_TAGGED - With MO_PAGE, indicates that the page includes a memory tag in bits 56-63.
@ MO_G3
MO_G3 - A symbol operand with this flag (granule 3) represents the high 16-bits of a 64-bit address,...
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
LLVM_ABI LegalityPredicate scalarOrEltWiderThan(unsigned TypeIdx, unsigned Size)
True iff the specified type index is a scalar or a vector with an element type that's wider than the ...
LLVM_ABI LegalityPredicate isPointerVector(unsigned TypeIdx)
True iff the specified type index is a vector of pointers (with any address space).
LLVM_ABI LegalityPredicate typeInSet(unsigned TypeIdx, std::initializer_list< LLT > TypesInit)
True iff the given type index is one of the specified types.
LLVM_ABI LegalityPredicate smallerThan(unsigned TypeIdx0, unsigned TypeIdx1)
True iff the first type index has a smaller total bit size than second type index.
LLVM_ABI LegalityPredicate atomicOrderingAtLeastOrStrongerThan(unsigned MMOIdx, AtomicOrdering Ordering)
True iff the specified MMO index has at an atomic ordering of at Ordering or stronger.
Predicate any(Predicate P0, Predicate P1)
True iff P0 or P1 are true.
LLVM_ABI LegalityPredicate isVector(unsigned TypeIdx)
True iff the specified type index is a vector.
Predicate all(Predicate P0, Predicate P1)
True iff P0 and P1 are true.
LLVM_ABI LegalityPredicate typeIs(unsigned TypeIdx, LLT TypesInit)
True iff the given type index is the specified type.
LLVM_ABI LegalityPredicate scalarWiderThan(unsigned TypeIdx, unsigned Size)
True iff the specified type index is a scalar that's wider than the given size.
LLVM_ABI LegalityPredicate scalarNarrowerThan(unsigned TypeIdx, unsigned Size)
True iff the specified type index is a scalar that's narrower than the given size.
@ Bitcast
Perform the operation on a different, but equivalently sized type.
LLVM_ABI LegalizeMutation moreElementsToNextPow2(unsigned TypeIdx, unsigned Min=0)
Add more elements to the type for the given type index to the next power of.
LLVM_ABI LegalizeMutation scalarize(unsigned TypeIdx)
Break up the vector type for the given type index into the element type.
LLVM_ABI LegalizeMutation changeElementTo(unsigned TypeIdx, unsigned FromTypeIdx)
Keep the same scalar or element type as the given type index.
LLVM_ABI LegalizeMutation widenScalarOrEltToNextPow2(unsigned TypeIdx, unsigned Min=0)
Widen the scalar type or vector element type for the given type index to the next power of 2.
LLVM_ABI LegalizeMutation changeTo(unsigned TypeIdx, LLT Ty)
Select this specific type for the given type index.
LLVM_ABI LegalizeMutation changeElementSizeTo(unsigned TypeIdx, unsigned FromTypeIdx)
Change the scalar size or element size to have the same scalar size as type index FromIndex.
operand_type_match m_Reg()
ConstantMatch< APInt > m_ICst(APInt &Cst)
bool mi_match(Reg R, const MachineRegisterInfo &MRI, Pattern &&P)
BinaryOp_match< LHS, RHS, TargetOpcode::G_PTR_ADD, false > m_GPtrAdd(const LHS &L, const RHS &R)
Invariant opcodes: All instruction sets have these as their low opcodes.
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::optional< APInt > isConstantOrConstantSplatVector(Register Def, const MachineRegisterInfo &MRI)
Determines if Def defines a constant integer or a splat vector of constant integers.
Definition Utils.cpp:1517
@ Offset
Definition DWP.cpp:578
LLVM_ABI void constrainSelectedInstRegOperands(MachineInstr &I, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI)
Mutate the newly-selected instruction I to constrain its (possibly generic) virtual register operands...
Definition Utils.cpp:159
constexpr bool isPowerOf2_64(uint64_t Value)
Return true if the argument is a power of two > 0 (64 bit edition.)
Definition MathExtras.h:285
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
uint64_t PowerOf2Ceil(uint64_t A)
Returns the power of two which is greater than or equal to the given value.
Definition MathExtras.h:380
std::function< bool(const LegalityQuery &)> LegalityPredicate
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:144
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
AtomicOrdering
Atomic ordering for LLVM's memory model.
@ Add
Sum of integers.
IntPtrTy
Definition InstrProf.h:82
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
constexpr bool isShiftedInt(int64_t x)
Checks if a signed integer is an N bit number shifted left by S.
Definition MathExtras.h:183
constexpr unsigned BitWidth
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
LLVM_ABI std::optional< ValueAndVReg > getIConstantVRegValWithLookThrough(Register VReg, const MachineRegisterInfo &MRI, bool LookThroughInstrs=true)
If VReg is defined by a statically evaluable chain of instructions rooted on a G_CONSTANT returns its...
Definition Utils.cpp:436
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1947
Align assumeAligned(uint64_t Value)
Treats the value 0 as a 1, so Align is always at least 1.
Definition Alignment.h:100
unsigned Log2(Align A)
Returns the log2 of the alignment.
Definition Alignment.h:197
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
The LegalityQuery object bundles together all the information that's needed to decide whether a given...
ArrayRef< MemDesc > MMODescrs
Operations which require memory can use this to place requirements on the memory type for each MMO.
ArrayRef< LLT > Types
This class contains a discriminated union of information about pointers in memory operands,...