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 .lower();
346
347 getActionDefinitionsBuilder({G_FSHL, G_FSHR})
348 .customFor({{i32, i32}, {i32, i64}, {i64, i64}})
349 .lower();
350
352 .legalFor({{i32, i64}, {i64, i64}})
353 .customIf([=](const LegalityQuery &Q) {
354 return Q.Types[0].isScalar() && Q.Types[1].getScalarSizeInBits() < 64;
355 })
356 .lower();
358
359 getActionDefinitionsBuilder({G_SBFX, G_UBFX})
360 .customFor({{s32, s32}, {s64, s64}});
361
362 auto always = [=](const LegalityQuery &Q) { return true; };
364 .legalFor(HasCSSC, {{i32, i32}, {i64, i64}})
365 .legalFor({{v8i8, v8i8}, {v16i8, v16i8}})
366 .customFor(!HasCSSC, {{s32, s32}, {s64, s64}})
367 .customFor({{s128, s128},
368 {v4s16, v4s16},
369 {v8s16, v8s16},
370 {v2s32, v2s32},
371 {v4s32, v4s32},
372 {v2s64, v2s64}})
373 .clampScalar(0, s32, s128)
376 .minScalarEltSameAsIf(always, 1, 0)
377 .maxScalarEltSameAsIf(always, 1, 0)
378 .clampNumElements(0, v8s8, v16s8)
379 .clampNumElements(0, v4s16, v8s16)
380 .clampNumElements(0, v2s32, v4s32)
381 .clampNumElements(0, v2s64, v2s64)
384
385 getActionDefinitionsBuilder({G_CTLZ, G_CTLS})
386 .legalFor({{i32, i32},
387 {i64, i64},
388 {v8i8, v8i8},
389 {v16i8, v16i8},
390 {v4i16, v4i16},
391 {v8i16, v8i16},
392 {v2i32, v2i32},
393 {v4i32, v4i32}})
394 .widenScalarToNextPow2(1, /*Min=*/32)
395 .clampScalar(1, s32, s64)
397 .clampNumElements(0, v8s8, v16s8)
398 .clampNumElements(0, v4s16, v8s16)
399 .clampNumElements(0, v2s32, v4s32)
402 .scalarSameSizeAs(0, 1);
403
404 getActionDefinitionsBuilder(G_INSERT_SUBVECTOR).lower();
405
406 getActionDefinitionsBuilder(G_CTLZ_ZERO_POISON).lower();
407
409 .lowerIf(isVector(0))
410 .widenScalarToNextPow2(1, /*Min=*/32)
411 .clampScalar(1, s32, s64)
412 .scalarSameSizeAs(0, 1)
413 .legalFor(HasCSSC, {s32, s64})
414 .customFor(!HasCSSC, {s32, s64});
415
416 getActionDefinitionsBuilder(G_CTTZ_ZERO_POISON).lower();
417
418 getActionDefinitionsBuilder(G_BITREVERSE)
419 .legalFor({i32, i64, v8i8, v16i8})
420 .widenScalarToNextPow2(0, /*Min = */ 32)
422 .clampScalar(0, s32, s64)
423 .clampNumElements(0, v8s8, v16s8)
424 .clampNumElements(0, v4s16, v8s16)
425 .clampNumElements(0, v2s32, v4s32)
426 .clampNumElements(0, v2s64, v2s64)
429 .lower();
430
431 getActionDefinitionsBuilder(G_CLMUL).legalFor({v8i8, v16i8});
432
434 .legalFor({i32, i64, v4i16, v8i16, v2i32, v4i32, v2i64})
436 .clampScalar(0, s32, s64)
437 .clampNumElements(0, v4s16, v8s16)
438 .clampNumElements(0, v2s32, v4s32)
439 .clampNumElements(0, v2s64, v2s64)
441
442 getActionDefinitionsBuilder({G_UADDSAT, G_SADDSAT, G_USUBSAT, G_SSUBSAT})
443 .legalFor({v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
444 .legalFor(HasSVE, {nxv16i8, nxv8i16, nxv4i32, nxv2i64})
445 .clampNumElements(0, v8s8, v16s8)
446 .clampNumElements(0, v4s16, v8s16)
447 .clampNumElements(0, v2s32, v4s32)
448 .clampMaxNumElements(0, s64, 2)
451 .lower();
452
454 {G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FMA, G_FSQRT, G_FMAXNUM, G_FMINNUM,
455 G_FMAXIMUM, G_FMINIMUM, G_FCEIL, G_FFLOOR, G_FRINT, G_FNEARBYINT,
456 G_INTRINSIC_TRUNC, G_INTRINSIC_ROUND, G_INTRINSIC_ROUNDEVEN})
457 .legalFor({f32, f64, v2f32, v4f32, v2f64})
458 .legalFor(HasFP16, {f16, v4f16, v8f16})
459 .libcallFor({f128})
460 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
462 [=](const LegalityQuery &Q) {
463 return (!HasFP16 && Q.Types[0].getScalarType().isFloat16()) ||
464 Q.Types[0].getScalarType().isBFloat16();
465 },
466 changeElementTo(0, f32))
467 .clampNumElements(0, v4s16, v8s16)
468 .clampNumElements(0, v2s32, v4s32)
469 .clampNumElements(0, v2s64, v2s64)
471
472 getActionDefinitionsBuilder({G_FABS, G_FNEG})
473 .legalFor({f32, f64, v2f32, v4f32, v2f64})
474 .legalFor(HasFP16, {f16, bf16, v4f16, v4bf16, v8f16, v8bf16})
475 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
477 .clampNumElements(0, v4s16, v8s16)
478 .clampNumElements(0, v2s32, v4s32)
479 .clampNumElements(0, v2s64, v2s64)
481 .lowerFor({f16, bf16, v4f16, v4bf16, v8f16, v8bf16});
482
483 getActionDefinitionsBuilder({G_FREM, G_FCOS, G_FSIN, G_FPOW, G_FLOG, G_FLOG2,
484 G_FLOG10, G_FTAN, G_FEXP, G_FEXP2, G_FEXP10,
485 G_FACOS, G_FASIN, G_FATAN, G_FATAN2, G_FCOSH,
486 G_FSINH, G_FTANH, G_FMODF})
487 .libcallFor({f32, f64, f128})
488 .widenScalarFor({f16, bf16}, changeElementTo(0, f32))
489 .scalarize(0);
490 getActionDefinitionsBuilder({G_FPOWI, G_FLDEXP})
491 .libcallFor({{f32, i32}, {f64, i32}, {f128, i32}})
492 .widenScalarFor({f16, bf16}, changeElementTo(0, f32))
493 .scalarize(0);
494
495 getActionDefinitionsBuilder({G_LROUND, G_INTRINSIC_LRINT})
496 .legalFor({{i32, f32}, {i32, f64}, {i64, f32}, {i64, f64}})
497 .legalFor(HasFP16, {{i32, f16}, {i64, f16}})
498 .minScalar(1, s32)
499 .libcallFor({{s64, s128}})
500 .lower();
501 getActionDefinitionsBuilder({G_LLROUND, G_INTRINSIC_LLRINT})
502 .legalFor({{i64, f32}, {i64, f64}})
503 .legalFor(HasFP16, {{i64, f16}})
504 .minScalar(0, s64)
505 .minScalar(1, s32)
506 .libcallFor({{s64, s128}})
507 .lower();
508
509 // TODO: Custom legalization for mismatched types.
510 getActionDefinitionsBuilder(G_FCOPYSIGN)
512 [](const LegalityQuery &Query) { return Query.Types[0].isScalar(); },
513 [=](const LegalityQuery &Query) {
514 const LLT Ty = Query.Types[0];
515 return std::pair(0, LLT::fixed_vector(Ty == s16 ? 4 : 2, Ty));
516 })
517 .lower();
518
520
521 for (unsigned Op : {G_SEXTLOAD, G_ZEXTLOAD}) {
522 auto &Actions = getActionDefinitionsBuilder(Op);
523
524 if (Op == G_SEXTLOAD)
526
527 // Atomics have zero extending behavior.
528 Actions
529 .legalForTypesWithMemDesc({{s32, p0, s8, 8},
530 {s32, p0, s16, 8},
531 {s32, p0, s32, 8},
532 {s64, p0, s8, 2},
533 {s64, p0, s16, 2},
534 {s64, p0, s32, 4},
535 {s64, p0, s64, 8},
536 {p0, p0, s64, 8},
537 {v2s32, p0, s64, 8}})
538 .widenScalarToNextPow2(0)
539 .clampScalar(0, s32, s64)
540 // TODO: We could support sum-of-pow2's but the lowering code doesn't know
541 // how to do that yet.
542 .unsupportedIfMemSizeNotPow2()
543 // Lower anything left over into G_*EXT and G_LOAD
544 .lower();
545 }
546
547 auto IsPtrVecPred = [=](const LegalityQuery &Query) {
548 const LLT &ValTy = Query.Types[0];
549 return ValTy.isPointerVector() && ValTy.getAddressSpace() == 0;
550 };
551
553 .customIf([=](const LegalityQuery &Query) {
554 return HasRCPC3 && Query.Types[0] == s128 &&
555 Query.MMODescrs[0].Ordering == AtomicOrdering::Acquire;
556 })
557 .customIf([=](const LegalityQuery &Query) {
558 return Query.Types[0] == s128 &&
559 Query.MMODescrs[0].Ordering != AtomicOrdering::NotAtomic;
560 })
561 .legalForTypesWithMemDesc({{s8, p0, s8, 8},
562 {s16, p0, s16, 8},
563 {s32, p0, s32, 8},
564 {s64, p0, s64, 8},
565 {p0, p0, s64, 8},
566 {s128, p0, s128, 8},
567 {v8s8, p0, s64, 8},
568 {v16s8, p0, s128, 8},
569 {v4s16, p0, s64, 8},
570 {v8s16, p0, s128, 8},
571 {v2s32, p0, s64, 8},
572 {v4s32, p0, s128, 8},
573 {v2s64, p0, s128, 8}})
574 // These extends are also legal
575 .legalForTypesWithMemDesc(
576 {{s32, p0, s8, 8}, {s32, p0, s16, 8}, {s64, p0, s32, 8}})
577 .legalForTypesWithMemDesc({
578 // SVE vscale x 128 bit base sizes
579 {nxv16s8, p0, nxv16s8, 8},
580 {nxv8s16, p0, nxv8s16, 8},
581 {nxv4s32, p0, nxv4s32, 8},
582 {nxv2s64, p0, nxv2s64, 8},
583 })
584 .widenScalarToNextPow2(0, /* MinSize = */ 8)
585 .clampMaxNumElements(0, s8, 16)
586 .clampMaxNumElements(0, s16, 8)
587 .clampMaxNumElements(0, s32, 4)
588 .clampMaxNumElements(0, s64, 2)
589 .clampMaxNumElements(0, p0, 2)
591 .clampScalar(0, s8, s64)
593 [=](const LegalityQuery &Query) {
594 // Clamp extending load results to 32-bits.
595 return Query.Types[0].isScalar() &&
596 Query.Types[0] != Query.MMODescrs[0].MemoryTy &&
597 Query.Types[0].getSizeInBits() > 32;
598 },
599 changeTo(0, s32))
600 // TODO: Use BITCAST for v2i8, v2i16 after G_TRUNC gets sorted out
601 .bitcastIf(typeInSet(0, {v4s8}),
602 [=](const LegalityQuery &Query) {
603 const LLT VecTy = Query.Types[0];
604 return std::pair(0, LLT::integer(VecTy.getSizeInBits()));
605 })
606 .customIf(IsPtrVecPred)
607 .scalarizeIf(typeInSet(0, {v2s16, v2s8}), 0)
608 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
609
611 .customIf([=](const LegalityQuery &Query) {
612 return HasRCPC3 && Query.Types[0] == s128 &&
613 Query.MMODescrs[0].Ordering == AtomicOrdering::Release;
614 })
615 .customIf([=](const LegalityQuery &Query) {
616 return Query.Types[0] == s128 &&
617 Query.MMODescrs[0].Ordering != AtomicOrdering::NotAtomic;
618 })
619 .widenScalarIf(
620 all(scalarNarrowerThan(0, 32),
622 changeElementSizeTo(0, s32))
624 {{s8, p0, s8, 8}, {s16, p0, s8, 8}, // truncstorei8 from s16
625 {s32, p0, s8, 8}, // truncstorei8 from s32
626 {s64, p0, s8, 8}, // truncstorei8 from s64
627 {s16, p0, s16, 8}, {s32, p0, s16, 8}, // truncstorei16 from s32
628 {s64, p0, s16, 8}, // truncstorei16 from s64
629 {s32, p0, s8, 8}, {s32, p0, s16, 8}, {s32, p0, s32, 8},
630 {s64, p0, s64, 8}, {s64, p0, s32, 8}, // truncstorei32 from s64
631 {p0, p0, s64, 8}, {s128, p0, s128, 8}, {v16s8, p0, s128, 8},
632 {v8s8, p0, s64, 8}, {v4s16, p0, s64, 8}, {v8s16, p0, s128, 8},
633 {v2s32, p0, s64, 8}, {v4s32, p0, s128, 8}, {v2s64, p0, s128, 8}})
634 .legalForTypesWithMemDesc({
635 // SVE vscale x 128 bit base sizes
636 // TODO: Add nxv2p0. Consider bitcastIf.
637 // See #92130
638 // https://github.com/llvm/llvm-project/pull/92130#discussion_r1616888461
639 {nxv16s8, p0, nxv16s8, 8},
640 {nxv8s16, p0, nxv8s16, 8},
641 {nxv4s32, p0, nxv4s32, 8},
642 {nxv2s64, p0, nxv2s64, 8},
643 })
644 .clampScalar(0, s8, s64)
645 .minScalarOrElt(0, s8)
646 .lowerIf([=](const LegalityQuery &Query) {
647 return Query.Types[0].isScalar() &&
648 Query.Types[0] != Query.MMODescrs[0].MemoryTy;
649 })
650 // Maximum: sN * k = 128
651 .clampMaxNumElements(0, s8, 16)
652 .clampMaxNumElements(0, s16, 8)
653 .clampMaxNumElements(0, s32, 4)
654 .clampMaxNumElements(0, s64, 2)
655 .clampMaxNumElements(0, p0, 2)
657 // TODO: Use BITCAST for v2i8, v2i16 after G_TRUNC gets sorted out
658 .bitcastIf(all(typeInSet(0, {v4s8}),
659 LegalityPredicate([=](const LegalityQuery &Query) {
660 return Query.Types[0].getSizeInBits() ==
661 Query.MMODescrs[0].MemoryTy.getSizeInBits();
662 })),
663 [=](const LegalityQuery &Query) {
664 const LLT VecTy = Query.Types[0];
665 return std::pair(0, LLT::integer(VecTy.getSizeInBits()));
666 })
667 .customIf(IsPtrVecPred)
668 .scalarizeIf(typeInSet(0, {v2s16, v2s8}), 0)
669 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
670 .lower();
671
672 getActionDefinitionsBuilder(G_INDEXED_STORE)
673 // Idx 0 == Ptr, Idx 1 == Val
674 // TODO: we can implement legalizations but as of now these are
675 // generated in a very specific way.
677 {p0, s8, s8, 8},
678 {p0, s16, s16, 8},
679 {p0, s32, s8, 8},
680 {p0, s32, s16, 8},
681 {p0, s32, s32, 8},
682 {p0, s64, s64, 8},
683 {p0, p0, p0, 8},
684 {p0, v8s8, v8s8, 8},
685 {p0, v16s8, v16s8, 8},
686 {p0, v4s16, v4s16, 8},
687 {p0, v8s16, v8s16, 8},
688 {p0, v2s32, v2s32, 8},
689 {p0, v4s32, v4s32, 8},
690 {p0, v2s64, v2s64, 8},
691 {p0, v2p0, v2p0, 8},
692 {p0, s128, s128, 8},
693 })
694 .unsupported();
695
696 auto IndexedLoadBasicPred = [=](const LegalityQuery &Query) {
697 LLT LdTy = Query.Types[0];
698 LLT PtrTy = Query.Types[1];
699 if (!llvm::is_contained(PackedVectorAllTypesVec, LdTy) &&
700 !llvm::is_contained(ScalarAndPtrTypesVec, LdTy) && LdTy != s128)
701 return false;
702 if (PtrTy != p0)
703 return false;
704 return true;
705 };
706 getActionDefinitionsBuilder(G_INDEXED_LOAD)
709 .legalIf(IndexedLoadBasicPred)
710 .unsupported();
711 getActionDefinitionsBuilder({G_INDEXED_SEXTLOAD, G_INDEXED_ZEXTLOAD})
712 .unsupportedIf(
714 .legalIf(all(typeInSet(0, {s16, s32, s64}),
715 LegalityPredicate([=](const LegalityQuery &Q) {
716 LLT LdTy = Q.Types[0];
717 LLT PtrTy = Q.Types[1];
718 LLT MemTy = Q.MMODescrs[0].MemoryTy;
719 if (PtrTy != p0)
720 return false;
721 if (LdTy == s16)
722 return MemTy == s8;
723 if (LdTy == s32)
724 return MemTy == s8 || MemTy == s16;
725 if (LdTy == s64)
726 return MemTy == s8 || MemTy == s16 || MemTy == s32;
727 return false;
728 })))
729 .unsupported();
730
731 // Constants
733 .legalFor({p0, s8, s16, s32, s64})
734 .widenScalarToNextPow2(0)
735 .clampScalar(0, s8, s64);
736 getActionDefinitionsBuilder(G_FCONSTANT)
737 .legalFor({s16, s32, s64, s128});
738
739 // FIXME: fix moreElementsToNextPow2
741 .legalFor({{i32, i32}, {i32, i64}, {i32, p0}})
743 .minScalarOrElt(1, s8)
744 .clampScalar(1, s32, s64)
745 .clampScalar(0, s32, s32)
748 [=](const LegalityQuery &Query) {
749 const LLT &Ty = Query.Types[0];
750 const LLT &SrcTy = Query.Types[1];
751 return Ty.isVector() && !SrcTy.isPointerVector() &&
752 Ty.getElementType() != SrcTy.getElementType();
753 },
754 0, 1)
755 .minScalarOrEltIf(
756 [=](const LegalityQuery &Query) { return Query.Types[1] == v2s16; },
757 1, s32)
758 .minScalarOrEltIf(
759 [=](const LegalityQuery &Query) {
760 return Query.Types[1].isPointerVector();
761 },
762 0, s64)
764 .clampNumElements(1, v8s8, v16s8)
765 .clampNumElements(1, v4s16, v8s16)
766 .clampNumElements(1, v2s32, v4s32)
767 .clampNumElements(1, v2s64, v2s64)
768 .clampNumElements(1, v2p0, v2p0)
769 .customIf(isVector(0));
770
772 .legalFor({{i32, f32},
773 {i32, f64},
774 {v4i32, v4f32},
775 {v2i32, v2f32},
776 {v2i64, v2f64}})
777 .legalFor(HasFP16, {{i32, f16}, {v4i16, v4f16}, {v8i16, v8f16}})
779 .clampScalar(0, s32, s32)
781 [=](const LegalityQuery &Q) {
782 return (!HasFP16 && Q.Types[1].getScalarType().isFloat16()) ||
783 Q.Types[1].getScalarType().isBFloat16();
784 },
785 changeElementTo(1, f32))
786 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1)
788 [=](const LegalityQuery &Query) {
789 const LLT &Ty = Query.Types[0];
790 const LLT &SrcTy = Query.Types[1];
791 return Ty.isVector() && !SrcTy.isPointerVector() &&
792 Ty.getElementType() != SrcTy.getElementType();
793 },
794 0, 1)
795 .clampNumElements(1, v4s16, v8s16)
796 .clampNumElements(1, v2s32, v4s32)
797 .clampMaxNumElements(1, s64, 2)
799 .libcallFor({{s32, s128}});
800
801 // Extensions
802 auto ExtLegalFunc = [=](const LegalityQuery &Query) {
803 unsigned DstSize = Query.Types[0].getSizeInBits();
804
805 // Handle legal vectors using legalFor
806 if (Query.Types[0].isVector())
807 return false;
808
809 if (DstSize < 8 || DstSize >= 128 || !isPowerOf2_32(DstSize))
810 return false; // Extending to a scalar s128 needs narrowing.
811
812 const LLT &SrcTy = Query.Types[1];
813
814 // Make sure we fit in a register otherwise. Don't bother checking that
815 // the source type is below 128 bits. We shouldn't be allowing anything
816 // through which is wider than the destination in the first place.
817 unsigned SrcSize = SrcTy.getSizeInBits();
818 if (SrcSize < 8 || !isPowerOf2_32(SrcSize))
819 return false;
820
821 return true;
822 };
823 getActionDefinitionsBuilder({G_ZEXT, G_SEXT, G_ANYEXT})
824 .legalIf(ExtLegalFunc)
825 .legalFor({{v8s16, v8s8}, {v4s32, v4s16}, {v2s64, v2s32}})
826 .clampScalar(0, s64, s64) // Just for s128, others are handled above.
828 .clampMaxNumElements(1, s8, 8)
829 .clampMaxNumElements(1, s16, 4)
830 .clampMaxNumElements(1, s32, 2)
831 // Tries to convert a large EXTEND into two smaller EXTENDs
832 .lowerIf([=](const LegalityQuery &Query) {
833 return (Query.Types[0].getScalarSizeInBits() >
834 Query.Types[1].getScalarSizeInBits() * 2) &&
835 Query.Types[0].isVector() &&
836 (Query.Types[1].getScalarSizeInBits() == 8 ||
837 Query.Types[1].getScalarSizeInBits() == 16);
838 })
839 .clampMinNumElements(1, s8, 8)
840 .clampMinNumElements(1, s16, 4)
842
844 .legalFor({{v8s8, v8s16}, {v4s16, v4s32}, {v2s32, v2s64}})
846 .clampMaxNumElements(0, s8, 8)
847 .clampMaxNumElements(0, s16, 4)
848 .clampMaxNumElements(0, s32, 2)
850 [=](const LegalityQuery &Query) { return Query.Types[0].isVector(); },
851 0, s8)
852 .lowerIf([=](const LegalityQuery &Query) {
853 LLT DstTy = Query.Types[0];
854 LLT SrcTy = Query.Types[1];
855 return DstTy.isVector() && SrcTy.getSizeInBits() > 128 &&
856 DstTy.getScalarSizeInBits() * 2 <= SrcTy.getScalarSizeInBits();
857 })
858 .clampMinNumElements(0, s8, 8)
859 .clampMinNumElements(0, s16, 4)
860 .alwaysLegal();
861
862 getActionDefinitionsBuilder({G_TRUNC_SSAT_S, G_TRUNC_SSAT_U, G_TRUNC_USAT_U})
863 .legalFor({{v8i8, v8i16}, {v4i16, v4i32}, {v2i32, v2i64}})
864 .clampNumElements(0, v8s8, v8s8)
865 .clampNumElements(0, v4s16, v4s16)
866 .clampNumElements(0, v2s32, v2s32)
867 .lower();
868
869 getActionDefinitionsBuilder(G_SEXT_INREG)
870 .legalFor({i32, i64, v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
871 .maxScalar(0, s64)
872 .clampNumElements(0, v8s8, v16s8)
873 .clampNumElements(0, v4s16, v8s16)
874 .clampNumElements(0, v2s32, v4s32)
875 .clampMaxNumElements(0, s64, 2)
876 .lower();
877
878 // FP conversions
880 .legalFor(
881 {{f16, f32}, {f16, f64}, {f32, f64}, {v4f16, v4f32}, {v2f32, v2f64}})
882 .legalFor(ST.hasBF16(), {{bf16, f32}, {v4bf16, v4f32}})
883 .libcallFor({{f16, f128}, {f32, f128}, {f64, f128}})
885 .customIf([](const LegalityQuery &Q) {
886 LLT DstTy = Q.Types[0];
887 LLT SrcTy = Q.Types[1];
888 return SrcTy.getScalarSizeInBits() == 64 &&
889 DstTy.getScalarSizeInBits() == 16;
890 })
891 .lowerFor({{bf16, f32}, {v4bf16, v4f32}})
892 // Clamp based on input
893 .clampNumElements(1, v4s32, v4s32)
894 .clampNumElements(1, v2s64, v2s64)
895 .scalarize(0);
896
897 getActionDefinitionsBuilder(G_FPEXT)
898 .legalFor({{f32, f16},
899 {f64, f16},
900 {f32, bf16},
901 {f64, f32},
902 {v4f32, v4f16},
903 {v4f32, v4bf16},
904 {v2f64, v2f32}})
905 .libcallFor({{f128, f64}, {f128, f32}, {f128, f16}})
908 [](const LegalityQuery &Q) {
909 LLT DstTy = Q.Types[0];
910 LLT SrcTy = Q.Types[1];
911 return SrcTy.isVector() && DstTy.isVector() &&
912 SrcTy.getScalarSizeInBits() == 16 &&
913 DstTy.getScalarSizeInBits() == 64;
914 },
915 changeElementTo(1, f32))
916 .clampNumElements(0, v4s32, v4s32)
917 .clampNumElements(0, v2s64, v2s64)
918 .scalarize(0);
919
920 // Conversions
921 getActionDefinitionsBuilder({G_FPTOSI, G_FPTOUI})
922 .legalFor({{i32, f32},
923 {i64, f32},
924 {i32, f64},
925 {i64, f64},
926 {v2i32, v2f32},
927 {v4i32, v4f32},
928 {v2i64, v2f64}})
929 .legalFor(HasFP16,
930 {{i32, f16}, {i64, f16}, {v4i16, v4f16}, {v8i16, v8f16}})
931 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
933 // The range of a fp16 value fits into an i17, so we can lower the width
934 // to i64.
936 [=](const LegalityQuery &Query) {
937 return Query.Types[1] == f16 && Query.Types[0].getSizeInBits() > 64;
938 },
939 changeTo(0, i64))
942 .minScalar(0, s32)
944 [HasFP16](const LegalityQuery &Query) {
945 return (!HasFP16 && Query.Types[1].getScalarType().isFloat16()) ||
946 Query.Types[1].getScalarType().isBFloat16();
947 },
948 changeElementTo(1, f32))
949 .widenScalarIf(
950 [=](const LegalityQuery &Query) {
951 return Query.Types[0].getScalarSizeInBits() <= 64 &&
952 Query.Types[0].getScalarSizeInBits() >
953 Query.Types[1].getScalarSizeInBits();
954 },
956 .widenScalarIf(
957 [=](const LegalityQuery &Query) {
958 return Query.Types[1].getScalarSizeInBits() <= 64 &&
959 Query.Types[0].getScalarSizeInBits() <
960 Query.Types[1].getScalarSizeInBits();
961 },
963 .clampNumElements(0, v4s16, v8s16)
964 .clampNumElements(0, v2s32, v4s32)
965 .clampMaxNumElements(0, s64, 2)
966 .libcallFor(
967 {{i32, f128}, {i64, f128}, {i128, f128}, {i128, f32}, {i128, f64}});
968
969 getActionDefinitionsBuilder({G_FPTOSI_SAT, G_FPTOUI_SAT})
970 .legalFor({{i32, f32},
971 {i64, f32},
972 {i32, f64},
973 {i64, f64},
974 {v2i32, v2f32},
975 {v4i32, v4f32},
976 {v2i64, v2f64}})
977 .legalFor(
978 HasFP16,
979 {{i16, f16}, {i32, f16}, {i64, f16}, {v4i16, v4f16}, {v8i16, v8f16}})
980 // Handle types larger than i64 by scalarizing/lowering.
981 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
983 // The range of a fp16 value fits into an i17, so we can lower the width
984 // to i64.
986 [=](const LegalityQuery &Query) {
987 return Query.Types[1] == f16 && Query.Types[0].getSizeInBits() > 64;
988 },
989 changeTo(0, i64))
990 .lowerIf(::any(scalarWiderThan(0, 64), scalarWiderThan(1, 64)), 0)
992 .widenScalarToNextPow2(0, /*MinSize=*/32)
993 .minScalar(0, s32)
995 [HasFP16](const LegalityQuery &Query) {
996 return (!HasFP16 && Query.Types[1].getScalarType().isFloat16()) ||
997 Query.Types[1].getScalarType().isBFloat16();
998 },
999 changeElementTo(1, f32))
1000 .widenScalarIf(
1001 [=](const LegalityQuery &Query) {
1002 unsigned ITySize = Query.Types[0].getScalarSizeInBits();
1003 return (ITySize == 16 || ITySize == 32 || ITySize == 64) &&
1004 ITySize > Query.Types[1].getScalarSizeInBits();
1005 },
1007 .widenScalarIf(
1008 [=](const LegalityQuery &Query) {
1009 unsigned FTySize = Query.Types[1].getScalarSizeInBits();
1010 return (FTySize == 16 || FTySize == 32 || FTySize == 64) &&
1011 Query.Types[0].getScalarSizeInBits() < FTySize;
1012 },
1015 .clampNumElements(0, v4s16, v8s16)
1016 .clampNumElements(0, v2s32, v4s32)
1017 .clampMaxNumElements(0, s64, 2);
1018
1019 getActionDefinitionsBuilder({G_SITOFP, G_UITOFP})
1020 .legalFor({{f32, i32},
1021 {f64, i32},
1022 {f32, i64},
1023 {f64, i64},
1024 {v2f32, v2i32},
1025 {v4f32, v4i32},
1026 {v2f64, v2i64}})
1027 .legalFor(HasFP16,
1028 {{f16, i32}, {f16, i64}, {v4f16, v4i16}, {v8f16, v8i16}})
1029 .unsupportedIf([&](const LegalityQuery &Query) {
1030 return Query.Types[0].getScalarType().isBFloat16();
1031 })
1032 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1)
1036 .minScalar(1, f32)
1037 .lowerIf([](const LegalityQuery &Query) {
1038 return Query.Types[1].isVector() &&
1039 Query.Types[1].getScalarSizeInBits() == 64 &&
1040 Query.Types[0].getScalarSizeInBits() == 16;
1041 })
1042 .widenScalarOrEltToNextPow2OrMinSize(0, /*MinSize=*/HasFP16 ? 16 : 32)
1043 .scalarizeIf(
1044 // v2i64->v2f32 needs to scalarize to avoid double-rounding issues.
1045 [](const LegalityQuery &Query) {
1046 return Query.Types[0].getScalarSizeInBits() == 32 &&
1047 Query.Types[1].getScalarSizeInBits() == 64;
1048 },
1049 0)
1050 .widenScalarIf(
1051 [](const LegalityQuery &Query) {
1052 return Query.Types[1].getScalarSizeInBits() <= 64 &&
1053 Query.Types[0].getScalarSizeInBits() <
1054 Query.Types[1].getScalarSizeInBits();
1055 },
1057 .widenScalarIf(
1058 [](const LegalityQuery &Query) {
1059 return Query.Types[0].getScalarSizeInBits() <= 64 &&
1060 Query.Types[0].getScalarSizeInBits() >
1061 Query.Types[1].getScalarSizeInBits();
1062 },
1064 .clampNumElements(0, v4s16, v8s16)
1065 .clampNumElements(0, v2s32, v4s32)
1066 .clampMaxNumElements(0, s64, 2)
1067 .libcallFor({{f16, i128},
1068 {f32, i128},
1069 {f64, i128},
1070 {f128, i128},
1071 {f128, i32},
1072 {f128, i64}});
1073
1074 // Control-flow
1075 getActionDefinitionsBuilder(G_BR).alwaysLegal();
1076 getActionDefinitionsBuilder(G_BRCOND)
1077 .legalFor({s32})
1078 .clampScalar(0, s32, s32);
1079 getActionDefinitionsBuilder(G_BRINDIRECT).legalFor({p0});
1080
1081 getActionDefinitionsBuilder(G_SELECT)
1082 .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
1083 .widenScalarToNextPow2(0)
1084 .clampScalar(0, s32, s64)
1085 .clampScalar(1, s32, s32)
1088 .lowerIf(isVector(0));
1089
1090 // Pointer-handling
1091 getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
1092
1093 if (TM.getCodeModel() == CodeModel::Small)
1094 getActionDefinitionsBuilder(G_GLOBAL_VALUE).custom();
1095 else
1096 getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0});
1097
1098 getActionDefinitionsBuilder(G_PTRAUTH_GLOBAL_VALUE)
1099 .legalIf(all(typeIs(0, p0), typeIs(1, p0)));
1100
1101 getActionDefinitionsBuilder(G_PTRTOINT)
1102 .legalFor({{i64, p0}, {v2i64, v2p0}})
1103 .widenScalarToNextPow2(0, 64)
1104 .clampScalar(0, s64, s64)
1105 .clampMaxNumElements(0, s64, 2);
1106
1107 getActionDefinitionsBuilder(G_INTTOPTR)
1108 .unsupportedIf([&](const LegalityQuery &Query) {
1109 return Query.Types[0].getSizeInBits() != Query.Types[1].getSizeInBits();
1110 })
1111 .legalFor({{p0, i64}, {v2p0, v2i64}})
1112 .clampMaxNumElements(1, s64, 2);
1113
1114 // Casts for 32 and 64-bit width type are just copies.
1115 // Same for 128-bit width type, except they are on the FPR bank.
1116 getActionDefinitionsBuilder(G_BITCAST)
1118 // Keeping 32-bit instructions legal to prevent regression in some tests
1119 .legalForCartesianProduct({s32, v2s16, v4s8})
1120 .legalForCartesianProduct({s64, v8s8, v4s16, v2s32})
1121 .legalForCartesianProduct({s128, v16s8, v8s16, v4s32, v2s64, v2p0})
1122 .customIf([=](const LegalityQuery &Query) {
1123 // Handle casts from i1 vectors to scalars.
1124 LLT DstTy = Query.Types[0];
1125 LLT SrcTy = Query.Types[1];
1126 return DstTy.isScalar() && SrcTy.isVector() &&
1127 SrcTy.getScalarSizeInBits() == 1;
1128 })
1129 .lowerIf([=](const LegalityQuery &Query) {
1130 return Query.Types[0].isVector() != Query.Types[1].isVector();
1131 })
1133 .clampNumElements(0, v8s8, v16s8)
1134 .clampNumElements(0, v4s16, v8s16)
1135 .clampNumElements(0, v2s32, v4s32)
1136 .clampMaxNumElements(0, s64, 2)
1137 .lower();
1138
1139 getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
1140
1141 // va_list must be a pointer, but most sized types are pretty easy to handle
1142 // as the destination.
1143 getActionDefinitionsBuilder(G_VAARG)
1144 .customForCartesianProduct({s8, s16, s32, s64, p0}, {p0})
1145 .clampScalar(0, s8, s64)
1146 .widenScalarToNextPow2(0, /*Min*/ 8);
1147
1148 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG_WITH_SUCCESS)
1149 .lowerIf(
1150 all(typeInSet(0, {s8, s16, s32, s64, s128}), typeIs(2, p0)));
1151
1152 bool UseOutlineAtomics = ST.outlineAtomics() && !ST.hasLSE();
1153
1154 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG)
1155 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1156 .customFor(!UseOutlineAtomics, {{s128, p0}})
1157 .libcallFor(UseOutlineAtomics,
1158 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}, {s128, p0}})
1159 .clampScalar(0, s32, s64);
1160
1161 getActionDefinitionsBuilder({G_ATOMICRMW_XCHG, G_ATOMICRMW_ADD,
1162 G_ATOMICRMW_SUB, G_ATOMICRMW_AND, G_ATOMICRMW_OR,
1163 G_ATOMICRMW_XOR})
1164 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1165 .libcallFor(UseOutlineAtomics,
1166 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}})
1167 .clampScalar(0, s32, s64);
1168
1169 // Do not outline these atomics operations, as per comment in
1170 // AArch64ISelLowering.cpp's shouldExpandAtomicRMWInIR().
1171 getActionDefinitionsBuilder(
1172 {G_ATOMICRMW_MIN, G_ATOMICRMW_MAX, G_ATOMICRMW_UMIN, G_ATOMICRMW_UMAX})
1173 .legalIf(all(typeInSet(0, {s32, s64}), typeIs(1, p0)))
1174 .clampScalar(0, s32, s64);
1175
1176 getActionDefinitionsBuilder(G_BLOCK_ADDR).legalFor({p0});
1177
1178 // Merge/Unmerge
1179 for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) {
1180 unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1;
1181 unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0;
1182 getActionDefinitionsBuilder(Op)
1183 .widenScalarToNextPow2(LitTyIdx, 8)
1184 .widenScalarToNextPow2(BigTyIdx, 32)
1185 .clampScalar(LitTyIdx, s8, s64)
1186 .clampScalar(BigTyIdx, s32, s128)
1187 .legalIf([=](const LegalityQuery &Q) {
1188 switch (Q.Types[BigTyIdx].getSizeInBits()) {
1189 case 32:
1190 case 64:
1191 case 128:
1192 break;
1193 default:
1194 return false;
1195 }
1196 switch (Q.Types[LitTyIdx].getSizeInBits()) {
1197 case 8:
1198 case 16:
1199 case 32:
1200 case 64:
1201 return true;
1202 default:
1203 return false;
1204 }
1205 });
1206 }
1207
1208 // TODO : nxv4s16, nxv2s16, nxv2s32
1209 getActionDefinitionsBuilder(G_EXTRACT_VECTOR_ELT)
1210 .legalFor(HasSVE, {{s16, nxv16s8, s64},
1211 {s16, nxv8s16, s64},
1212 {s32, nxv4s32, s64},
1213 {s64, nxv2s64, s64}})
1214 .unsupportedIf([=](const LegalityQuery &Query) {
1215 const LLT &EltTy = Query.Types[1].getElementType();
1216 if (Query.Types[1].isScalableVector())
1217 return false;
1218 return Query.Types[0] != EltTy;
1219 })
1220 .minScalar(2, s64)
1221 .customIf([=](const LegalityQuery &Query) {
1222 const LLT &VecTy = Query.Types[1];
1223 return VecTy == v8s8 || VecTy == v16s8 || VecTy == v2s16 ||
1224 VecTy == v4s16 || VecTy == v8s16 || VecTy == v2s32 ||
1225 VecTy == v4s32 || VecTy == v2s64 || VecTy == v2p0;
1226 })
1227 .minScalarOrEltIf(
1228 [=](const LegalityQuery &Query) {
1229 // We want to promote to <M x s1> to <M x s64> if that wouldn't
1230 // cause the total vec size to be > 128b.
1231 return Query.Types[1].isFixedVector() &&
1232 Query.Types[1].getNumElements() <= 2;
1233 },
1234 0, s64)
1235 .minScalarOrEltIf(
1236 [=](const LegalityQuery &Query) {
1237 return Query.Types[1].isFixedVector() &&
1238 Query.Types[1].getNumElements() <= 4;
1239 },
1240 0, s32)
1241 .minScalarOrEltIf(
1242 [=](const LegalityQuery &Query) {
1243 return Query.Types[1].isFixedVector() &&
1244 Query.Types[1].getNumElements() <= 8;
1245 },
1246 0, s16)
1247 .minScalarOrEltIf(
1248 [=](const LegalityQuery &Query) {
1249 return Query.Types[1].isFixedVector() &&
1250 Query.Types[1].getNumElements() <= 16;
1251 },
1252 0, s8)
1253 .minScalarOrElt(0, s8) // Worst case, we need at least s8.
1254 .moreElementsToNextPow2(1)
1255 .clampMaxNumElements(1, s64, 2)
1256 .clampMaxNumElements(1, s32, 4)
1257 .clampMaxNumElements(1, s16, 8)
1258 .clampMaxNumElements(1, s8, 16)
1259 .clampMaxNumElements(1, p0, 2)
1260 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1);
1261
1262 getActionDefinitionsBuilder(G_INSERT_VECTOR_ELT)
1263 .legalIf(
1264 typeInSet(0, {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64, v2p0}))
1265 .legalFor(HasSVE, {{nxv16s8, s32, s64},
1266 {nxv8s16, s32, s64},
1267 {nxv4s32, s32, s64},
1268 {nxv2s64, s64, s64}})
1270 .widenVectorEltsToVectorMinSize(0, 64)
1271 .clampNumElements(0, v8s8, v16s8)
1272 .clampNumElements(0, v4s16, v8s16)
1273 .clampNumElements(0, v2s32, v4s32)
1274 .clampMaxNumElements(0, s64, 2)
1275 .clampMaxNumElements(0, p0, 2)
1276 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
1277
1278 getActionDefinitionsBuilder(G_BUILD_VECTOR)
1279 .legalFor({{v8s8, s8},
1280 {v16s8, s8},
1281 {v4s16, s16},
1282 {v8s16, s16},
1283 {v2s32, s32},
1284 {v4s32, s32},
1285 {v2s64, s64},
1286 {v2p0, p0}})
1287 .clampNumElements(0, v4s32, v4s32)
1288 .clampNumElements(0, v2s64, v2s64)
1289 .minScalarOrElt(0, s8)
1290 .widenVectorEltsToVectorMinSize(0, 64)
1291 .widenScalarOrEltToNextPow2(0)
1292 .minScalarSameAs(1, 0);
1293
1294 getActionDefinitionsBuilder(G_BUILD_VECTOR_TRUNC).lower();
1295
1296 getActionDefinitionsBuilder(G_SHUFFLE_VECTOR)
1297 .legalIf([=](const LegalityQuery &Query) {
1298 const LLT &DstTy = Query.Types[0];
1299 const LLT &SrcTy = Query.Types[1];
1300 // For now just support the TBL2 variant which needs the source vectors
1301 // to be the same size as the dest.
1302 if (DstTy != SrcTy)
1303 return false;
1304 return llvm::is_contained(
1305 {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64}, DstTy);
1306 })
1307 .moreElementsIf(
1308 [](const LegalityQuery &Query) {
1309 return Query.Types[0].getNumElements() >
1310 Query.Types[1].getNumElements();
1311 },
1312 changeTo(1, 0))
1314 .moreElementsIf(
1315 [](const LegalityQuery &Query) {
1316 return Query.Types[0].getNumElements() <
1317 Query.Types[1].getNumElements();
1318 },
1319 changeTo(0, 1))
1320 .widenScalarOrEltToNextPow2OrMinSize(0, 8)
1321 .clampNumElements(0, v8s8, v16s8)
1322 .clampNumElements(0, v4s16, v8s16)
1323 .clampNumElements(0, v4s32, v4s32)
1324 .clampNumElements(0, v2s64, v2s64)
1325 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
1326 .bitcastIf(isPointerVector(0), [=](const LegalityQuery &Query) {
1327 // Bitcast pointers vector to i64.
1328 const LLT DstTy = Query.Types[0];
1329 return std::pair(
1330 0, LLT::vector(DstTy.getElementCount(), LLT::integer(64)));
1331 });
1332
1333 getActionDefinitionsBuilder(G_CONCAT_VECTORS)
1334 .legalFor({{v16s8, v8s8}, {v8s16, v4s16}, {v4s32, v2s32}})
1335 .customIf([=](const LegalityQuery &Query) {
1336 return Query.Types[0].isFixedVector() &&
1337 Query.Types[0].getScalarSizeInBits() < 8;
1338 })
1339 .bitcastIf(
1340 [=](const LegalityQuery &Query) {
1341 return Query.Types[0].isFixedVector() &&
1342 Query.Types[1].isFixedVector() &&
1343 Query.Types[0].getScalarSizeInBits() >= 8 &&
1344 isPowerOf2_64(Query.Types[0].getScalarSizeInBits()) &&
1345 Query.Types[0].getSizeInBits() <= 128 &&
1346 Query.Types[1].getSizeInBits() <= 64;
1347 },
1348 [=](const LegalityQuery &Query) {
1349 const LLT DstTy = Query.Types[0];
1350 const LLT SrcTy = Query.Types[1];
1351 return std::pair(
1352 0, DstTy.changeElementSize(SrcTy.getSizeInBits())
1355 SrcTy.getNumElements())));
1356 });
1357
1358 getActionDefinitionsBuilder(G_EXTRACT_SUBVECTOR)
1359 .legalFor({{v8s8, v16s8}, {v4s16, v8s16}, {v2s32, v4s32}})
1361 .clampMaxNumElements(0, s8, 16)
1362 .clampMaxNumElements(0, s16, 8)
1363 .clampMaxNumElements(0, s32, 4)
1364 .clampNumElements(1, v8s8, v16s8)
1365 .clampNumElements(1, v4s16, v8s16)
1366 .clampNumElements(1, v2s32, v4s32)
1367 .lower()
1368 .immIdx(0); // Inform verifier imm idx 0 is handled.
1369
1370 // TODO: {nxv16s8, s8}, {nxv8s16, s16}
1371 getActionDefinitionsBuilder(G_SPLAT_VECTOR)
1372 .legalFor(HasSVE, {{nxv4s32, s32}, {nxv2s64, s64}});
1373
1374 getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
1375
1376 getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, s64}});
1377
1378 getActionDefinitionsBuilder({G_TRAP, G_DEBUGTRAP, G_UBSANTRAP}).alwaysLegal();
1379
1380 getActionDefinitionsBuilder(G_DYN_STACKALLOC).custom();
1381
1382 getActionDefinitionsBuilder({G_STACKSAVE, G_STACKRESTORE}).lower();
1383
1384 if (ST.hasMOPS()) {
1385 // G_BZERO is not supported. Currently it is only emitted by
1386 // PreLegalizerCombiner for G_MEMSET with zero constant.
1387 getActionDefinitionsBuilder(G_BZERO).unsupported();
1388
1389 getActionDefinitionsBuilder(G_MEMSET)
1390 .legalForCartesianProduct({p0}, {s64}, {s64})
1391 .customForCartesianProduct({p0}, {s8}, {s64})
1392 .immIdx(0); // Inform verifier imm idx 0 is handled.
1393
1394 getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
1395 .legalForCartesianProduct({p0}, {p0}, {s64})
1396 .immIdx(0); // Inform verifier imm idx 0 is handled.
1397
1398 // G_MEMCPY_INLINE does not have a tailcall immediate
1399 getActionDefinitionsBuilder(G_MEMCPY_INLINE)
1400 .legalForCartesianProduct({p0}, {p0}, {s64});
1401
1402 getActionDefinitionsBuilder(G_MEMSET_INLINE)
1403 .legalForCartesianProduct({p0}, {s64}, {s64})
1404 .customForCartesianProduct({p0}, {s8}, {s64});
1405 } else {
1406 getActionDefinitionsBuilder({G_BZERO, G_MEMCPY, G_MEMMOVE, G_MEMSET})
1407 .libcall();
1408 }
1409
1410 // For fadd reductions we have pairwise operations available. We treat the
1411 // usual legal types as legal and handle the lowering to pairwise instructions
1412 // later.
1413 getActionDefinitionsBuilder(G_VECREDUCE_FADD)
1414 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1415 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
1416 .widenScalarIf(
1417 [HasFP16](const LegalityQuery &Query) {
1418 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1419 Query.Types[0].getScalarType().isBFloat16();
1420 },
1421 changeElementTo(0, f32))
1422 .clampMaxNumElements(1, s64, 2)
1423 .clampMaxNumElements(1, s32, 4)
1424 .clampMaxNumElements(1, s16, 8)
1425 .moreElementsToNextPow2(1)
1426 .scalarize(1)
1427 .lower();
1428
1429 // For fmul reductions we need to split up into individual operations. We
1430 // clamp to 128 bit vectors then to 64bit vectors to produce a cascade of
1431 // smaller types, followed by scalarizing what remains.
1432 getActionDefinitionsBuilder(G_VECREDUCE_FMUL)
1433 .widenScalarIf(
1434 [HasFP16](const LegalityQuery &Query) {
1435 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1436 Query.Types[0].getScalarType().isBFloat16();
1437 },
1438 changeElementTo(0, f32))
1439 .clampMaxNumElements(1, s64, 2)
1440 .clampMaxNumElements(1, s32, 4)
1441 .clampMaxNumElements(1, s16, 8)
1442 .clampMaxNumElements(1, s32, 2)
1443 .clampMaxNumElements(1, s16, 4)
1444 .scalarize(1)
1445 .lower();
1446
1447 getActionDefinitionsBuilder({G_VECREDUCE_SEQ_FADD, G_VECREDUCE_SEQ_FMUL})
1448 .scalarize(2)
1449 .lower();
1450
1451 getActionDefinitionsBuilder(G_VECREDUCE_ADD)
1452 .legalFor({{i8, v8i8},
1453 {i8, v16i8},
1454 {i16, v4i16},
1455 {i16, v8i16},
1456 {i32, v2i32},
1457 {i32, v4i32},
1458 {i64, v2i64}})
1460 .clampMaxNumElements(1, s64, 2)
1461 .clampMaxNumElements(1, s32, 4)
1462 .clampMaxNumElements(1, s16, 8)
1463 .clampMaxNumElements(1, s8, 16)
1464 .widenVectorEltsToVectorMinSize(1, 64)
1465 .scalarize(1);
1466
1467 getActionDefinitionsBuilder({G_VECREDUCE_FMIN, G_VECREDUCE_FMAX,
1468 G_VECREDUCE_FMINIMUM, G_VECREDUCE_FMAXIMUM})
1469 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1470 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
1471 .widenScalarIf(
1472 [HasFP16](const LegalityQuery &Query) {
1473 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1474 Query.Types[0].getScalarType().isBFloat16();
1475 },
1476 changeElementTo(0, f32))
1477 .clampMaxNumElements(1, s64, 2)
1478 .clampMaxNumElements(1, s32, 4)
1479 .clampMaxNumElements(1, s16, 8)
1480 .scalarize(1)
1481 .lower();
1482
1483 getActionDefinitionsBuilder(G_VECREDUCE_MUL)
1484 .clampMaxNumElements(1, s32, 2)
1485 .clampMaxNumElements(1, s16, 4)
1486 .clampMaxNumElements(1, s8, 8)
1487 .scalarize(1)
1488 .lower();
1489
1490 getActionDefinitionsBuilder(
1491 {G_VECREDUCE_SMIN, G_VECREDUCE_SMAX, G_VECREDUCE_UMIN, G_VECREDUCE_UMAX})
1492 .legalFor({{i8, v8i8},
1493 {i8, v16i8},
1494 {i16, v4i16},
1495 {i16, v8i16},
1496 {i32, v2i32},
1497 {i32, v4i32}})
1498 .moreElementsIf(
1499 [=](const LegalityQuery &Query) {
1500 return Query.Types[1].isVector() &&
1501 Query.Types[1].getElementType() != s8 &&
1502 Query.Types[1].getNumElements() & 1;
1503 },
1505 .clampMaxNumElements(1, s64, 2)
1506 .clampMaxNumElements(1, s32, 4)
1507 .clampMaxNumElements(1, s16, 8)
1508 .clampMaxNumElements(1, s8, 16)
1509 .scalarize(1)
1510 .lower();
1511
1512 getActionDefinitionsBuilder(
1513 {G_VECREDUCE_OR, G_VECREDUCE_AND, G_VECREDUCE_XOR})
1514 // Try to break down into smaller vectors as long as they're at least 64
1515 // bits. This lets us use vector operations for some parts of the
1516 // reduction.
1517 .fewerElementsIf(
1518 [=](const LegalityQuery &Q) {
1519 LLT SrcTy = Q.Types[1];
1520 if (SrcTy.isScalar())
1521 return false;
1522 if (!isPowerOf2_32(SrcTy.getNumElements()))
1523 return false;
1524 // We can usually perform 64b vector operations.
1525 return SrcTy.getSizeInBits() > 64;
1526 },
1527 [=](const LegalityQuery &Q) {
1528 LLT SrcTy = Q.Types[1];
1529 return std::make_pair(1, SrcTy.divide(2));
1530 })
1531 .scalarize(1)
1532 .lower();
1533
1534 // TODO: Update this to correct handling when adding AArch64/SVE support.
1535 getActionDefinitionsBuilder(G_VECTOR_COMPRESS).lower();
1536
1537 // Access to floating-point environment.
1538 getActionDefinitionsBuilder({G_GET_FPENV, G_SET_FPENV, G_RESET_FPENV,
1539 G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE})
1540 .libcall();
1541
1542 getActionDefinitionsBuilder({G_GET_ROUNDING, G_SET_ROUNDING})
1543 .customFor({s32});
1544
1545 getActionDefinitionsBuilder(G_IS_FPCLASS).lower();
1546
1547 getActionDefinitionsBuilder(G_PREFETCH).custom();
1548
1549 getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
1550
1551 getActionDefinitionsBuilder({G_INTRINSIC, G_INTRINSIC_W_SIDE_EFFECTS})
1552 .alwaysLegal();
1553 getActionDefinitionsBuilder(G_FENCE).alwaysLegal();
1554 getActionDefinitionsBuilder(G_INVOKE_REGION_START).alwaysLegal();
1555
1556 verify(*ST.getInstrInfo());
1557}
1558
1561 LostDebugLocObserver &LocObserver) const {
1562 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
1563 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
1564 GISelChangeObserver &Observer = Helper.Observer;
1565 switch (MI.getOpcode()) {
1566 default:
1567 // No idea what to do.
1568 return false;
1569 case TargetOpcode::G_VAARG:
1570 return legalizeVaArg(MI, MRI, MIRBuilder);
1571 case TargetOpcode::G_LOAD:
1572 case TargetOpcode::G_STORE:
1573 return legalizeLoadStore(MI, MRI, MIRBuilder, Observer);
1574 case TargetOpcode::G_SHL:
1575 case TargetOpcode::G_ASHR:
1576 case TargetOpcode::G_LSHR:
1577 return legalizeShlAshrLshr(MI, MRI, MIRBuilder, Observer);
1578 case TargetOpcode::G_GLOBAL_VALUE:
1579 return legalizeSmallCMGlobalValue(MI, MRI, MIRBuilder, Observer);
1580 case TargetOpcode::G_SBFX:
1581 case TargetOpcode::G_UBFX:
1582 return legalizeBitfieldExtract(MI, MRI, Helper);
1583 case TargetOpcode::G_FSHL:
1584 case TargetOpcode::G_FSHR:
1585 return legalizeFunnelShift(MI, MRI, MIRBuilder, Observer, Helper);
1586 case TargetOpcode::G_ROTR:
1587 return legalizeRotate(MI, MRI, Helper);
1588 case TargetOpcode::G_CTPOP:
1589 return legalizeCTPOP(MI, MRI, Helper);
1590 case TargetOpcode::G_ATOMIC_CMPXCHG:
1591 return legalizeAtomicCmpxchg128(MI, MRI, Helper);
1592 case TargetOpcode::G_CTTZ:
1593 return legalizeCTTZ(MI, Helper);
1594 case TargetOpcode::G_BZERO:
1595 case TargetOpcode::G_MEMCPY:
1596 case TargetOpcode::G_MEMMOVE:
1597 case TargetOpcode::G_MEMSET:
1598 case TargetOpcode::G_MEMSET_INLINE:
1599 return legalizeMemOps(MI, Helper);
1600 case TargetOpcode::G_EXTRACT_VECTOR_ELT:
1601 return legalizeExtractVectorElt(MI, MRI, Helper);
1602 case TargetOpcode::G_DYN_STACKALLOC:
1603 return legalizeDynStackAlloc(MI, Helper);
1604 case TargetOpcode::G_PREFETCH:
1605 return legalizePrefetch(MI, Helper);
1606 case TargetOpcode::G_ABS:
1607 return Helper.lowerAbsToCNeg(MI);
1608 case TargetOpcode::G_ICMP:
1609 return legalizeICMP(MI, MRI, MIRBuilder);
1610 case TargetOpcode::G_BITCAST:
1611 return legalizeBitcast(MI, Helper);
1612 case TargetOpcode::G_CONCAT_VECTORS:
1613 return legalizeConcatVectors(MI, MRI, MIRBuilder);
1614 case TargetOpcode::G_FPTRUNC:
1615 // In order to lower f16 to f64 properly, we need to use f32 as an
1616 // intermediary
1617 return legalizeFptrunc(MI, MIRBuilder, MRI);
1618 case TargetOpcode::G_GET_ROUNDING:
1619 return legalizeGetRounding(MI, MIRBuilder, MRI, Helper);
1620 case TargetOpcode::G_SET_ROUNDING:
1621 return legalizeSetRounding(MI, MIRBuilder, MRI, Helper);
1622 }
1623
1624 llvm_unreachable("expected switch to return");
1625}
1626
1627bool AArch64LegalizerInfo::legalizeBitcast(MachineInstr &MI,
1628 LegalizerHelper &Helper) const {
1629 assert(MI.getOpcode() == TargetOpcode::G_BITCAST && "Unexpected opcode");
1630 auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
1631 // We're trying to handle casts from i1 vectors to scalars but reloading from
1632 // stack.
1633 if (!DstTy.isScalar() || !SrcTy.isVector() ||
1634 SrcTy.getElementType() != LLT::scalar(1))
1635 return false;
1636
1637 Helper.createStackStoreLoad(DstReg, SrcReg);
1638 MI.eraseFromParent();
1639 return true;
1640}
1641
1642bool AArch64LegalizerInfo::legalizeFunnelShift(MachineInstr &MI,
1644 MachineIRBuilder &MIRBuilder,
1645 GISelChangeObserver &Observer,
1646 LegalizerHelper &Helper) const {
1647 assert(MI.getOpcode() == TargetOpcode::G_FSHL ||
1648 MI.getOpcode() == TargetOpcode::G_FSHR);
1649
1650 // Keep as G_FSHR if shift amount is a G_CONSTANT, else use generic
1651 // lowering
1652 Register ShiftNo = MI.getOperand(3).getReg();
1653 LLT ShiftTy = MRI.getType(ShiftNo);
1654 auto VRegAndVal = getIConstantVRegValWithLookThrough(ShiftNo, MRI);
1655
1656 // Adjust shift amount according to Opcode (FSHL/FSHR)
1657 // Convert FSHL to FSHR
1658 LLT OperationTy = MRI.getType(MI.getOperand(0).getReg());
1659 APInt BitWidth(ShiftTy.getSizeInBits(), OperationTy.getSizeInBits(), false);
1660
1661 // Lower non-constant shifts and leave zero shifts to the optimizer.
1662 if (!VRegAndVal || VRegAndVal->Value.urem(BitWidth) == 0)
1663 return (Helper.lowerFunnelShiftAsShifts(MI) ==
1665
1666 APInt Amount = VRegAndVal->Value.urem(BitWidth);
1667
1668 Amount = MI.getOpcode() == TargetOpcode::G_FSHL ? BitWidth - Amount : Amount;
1669
1670 // If the instruction is G_FSHR, has a 64-bit G_CONSTANT for shift amount
1671 // in the range of 0 <-> BitWidth, it is legal
1672 if (ShiftTy.getSizeInBits() == 64 && MI.getOpcode() == TargetOpcode::G_FSHR &&
1673 VRegAndVal->Value.ult(BitWidth))
1674 return true;
1675
1676 // Cast the ShiftNumber to a 64-bit type
1677 auto Cast64 = MIRBuilder.buildConstant(LLT::integer(64), Amount.zext(64));
1678
1679 if (MI.getOpcode() == TargetOpcode::G_FSHR) {
1680 Observer.changingInstr(MI);
1681 MI.getOperand(3).setReg(Cast64.getReg(0));
1682 Observer.changedInstr(MI);
1683 }
1684 // If Opcode is FSHL, remove the FSHL instruction and create a FSHR
1685 // instruction
1686 else if (MI.getOpcode() == TargetOpcode::G_FSHL) {
1687 MIRBuilder.buildInstr(TargetOpcode::G_FSHR, {MI.getOperand(0).getReg()},
1688 {MI.getOperand(1).getReg(), MI.getOperand(2).getReg(),
1689 Cast64.getReg(0)});
1690 MI.eraseFromParent();
1691 }
1692 return true;
1693}
1694
1695bool AArch64LegalizerInfo::legalizeICMP(MachineInstr &MI,
1697 MachineIRBuilder &MIRBuilder) const {
1698 Register DstReg = MI.getOperand(0).getReg();
1699 Register SrcReg1 = MI.getOperand(2).getReg();
1700 Register SrcReg2 = MI.getOperand(3).getReg();
1701 LLT DstTy = MRI.getType(DstReg);
1702 LLT SrcTy = MRI.getType(SrcReg1);
1703
1704 // Check the vector types are legal
1705 if (DstTy.getScalarSizeInBits() != SrcTy.getScalarSizeInBits() ||
1706 DstTy.getNumElements() != SrcTy.getNumElements() ||
1707 (DstTy.getSizeInBits() != 64 && DstTy.getSizeInBits() != 128))
1708 return false;
1709
1710 // Lowers G_ICMP NE => G_ICMP EQ to allow better pattern matching for
1711 // following passes
1712 CmpInst::Predicate Pred = (CmpInst::Predicate)MI.getOperand(1).getPredicate();
1713 if (Pred != CmpInst::ICMP_NE)
1714 return true;
1715 Register CmpReg =
1716 MIRBuilder
1717 .buildICmp(CmpInst::ICMP_EQ, MRI.getType(DstReg), SrcReg1, SrcReg2)
1718 .getReg(0);
1719 MIRBuilder.buildNot(DstReg, CmpReg);
1720
1721 MI.eraseFromParent();
1722 return true;
1723}
1724
1725bool AArch64LegalizerInfo::legalizeRotate(MachineInstr &MI,
1727 LegalizerHelper &Helper) const {
1728 // To allow for imported patterns to match, we ensure that the rotate amount
1729 // is 64b with an extension.
1730 Register AmtReg = MI.getOperand(2).getReg();
1731 LLT AmtTy = MRI.getType(AmtReg);
1732 (void)AmtTy;
1733 assert(AmtTy.isScalar() && "Expected a scalar rotate");
1734 assert(AmtTy.getSizeInBits() < 64 && "Expected this rotate to be legal");
1735 auto NewAmt = Helper.MIRBuilder.buildZExt(LLT::integer(64), AmtReg);
1736 Helper.Observer.changingInstr(MI);
1737 MI.getOperand(2).setReg(NewAmt.getReg(0));
1738 Helper.Observer.changedInstr(MI);
1739 return true;
1740}
1741
1742bool AArch64LegalizerInfo::legalizeSmallCMGlobalValue(
1744 GISelChangeObserver &Observer) const {
1745 assert(MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
1746 // We do this custom legalization to convert G_GLOBAL_VALUE into target ADRP +
1747 // G_ADD_LOW instructions.
1748 // By splitting this here, we can optimize accesses in the small code model by
1749 // folding in the G_ADD_LOW into the load/store offset.
1750 auto &GlobalOp = MI.getOperand(1);
1751 // Don't modify an intrinsic call.
1752 if (GlobalOp.isSymbol())
1753 return true;
1754 const auto* GV = GlobalOp.getGlobal();
1755 if (GV->isThreadLocal())
1756 return true; // Don't want to modify TLS vars.
1757
1758 auto &TM = ST->getTargetLowering()->getTargetMachine();
1759 unsigned OpFlags = ST->ClassifyGlobalReference(GV, TM);
1760
1761 if (OpFlags & AArch64II::MO_GOT)
1762 return true;
1763
1764 auto Offset = GlobalOp.getOffset();
1765 Register DstReg = MI.getOperand(0).getReg();
1766 auto ADRP = MIRBuilder.buildInstr(AArch64::ADRP, {LLT::pointer(0, 64)}, {})
1767 .addGlobalAddress(GV, Offset, OpFlags | AArch64II::MO_PAGE);
1768 // Set the regclass on the dest reg too.
1769 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1770
1771 // MO_TAGGED on the page indicates a tagged address. Set the tag now. We do so
1772 // by creating a MOVK that sets bits 48-63 of the register to (global address
1773 // + 0x100000000 - PC) >> 48. The additional 0x100000000 offset here is to
1774 // prevent an incorrect tag being generated during relocation when the
1775 // global appears before the code section. Without the offset, a global at
1776 // `0x0f00'0000'0000'1000` (i.e. at `0x1000` with tag `0xf`) that's referenced
1777 // by code at `0x2000` would result in `0x0f00'0000'0000'1000 - 0x2000 =
1778 // 0x0eff'ffff'ffff'f000`, meaning the tag would be incorrectly set to `0xe`
1779 // instead of `0xf`.
1780 // This assumes that we're in the small code model so we can assume a binary
1781 // size of <= 4GB, which makes the untagged PC relative offset positive. The
1782 // binary must also be loaded into address range [0, 2^48). Both of these
1783 // properties need to be ensured at runtime when using tagged addresses.
1784 if (OpFlags & AArch64II::MO_TAGGED) {
1785 assert(!Offset &&
1786 "Should not have folded in an offset for a tagged global!");
1787 ADRP = MIRBuilder.buildInstr(AArch64::MOVKXi, {LLT::pointer(0, 64)}, {ADRP})
1788 .addGlobalAddress(GV, 0x100000000,
1790 .addImm(48);
1791 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1792 }
1793
1794 MIRBuilder.buildInstr(AArch64::G_ADD_LOW, {DstReg}, {ADRP})
1795 .addGlobalAddress(GV, Offset,
1797 MI.eraseFromParent();
1798 return true;
1799}
1800
1802 MachineInstr &MI) const {
1803 MachineIRBuilder &MIB = Helper.MIRBuilder;
1804 MachineRegisterInfo &MRI = *MIB.getMRI();
1805
1806 auto LowerUnaryOp = [&MI, &MIB](unsigned Opcode) {
1807 MIB.buildInstr(Opcode, {MI.getOperand(0)}, {MI.getOperand(2)});
1808 MI.eraseFromParent();
1809 return true;
1810 };
1811 auto LowerBinOp = [&MI, &MIB](unsigned Opcode) {
1812 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1813 {MI.getOperand(2), MI.getOperand(3)});
1814 MI.eraseFromParent();
1815 return true;
1816 };
1817 auto LowerTriOp = [&MI, &MIB](unsigned Opcode) {
1818 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1819 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4)});
1820 MI.eraseFromParent();
1821 return true;
1822 };
1823
1824 Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
1825 switch (IntrinsicID) {
1826 case Intrinsic::vacopy: {
1827 unsigned PtrSize = ST->isTargetILP32() ? 4 : 8;
1828 unsigned VaListSize =
1829 (ST->isTargetDarwin() || ST->isTargetWindows())
1830 ? PtrSize
1831 : ST->isTargetILP32() ? 20 : 32;
1832
1833 MachineFunction &MF = *MI.getMF();
1835 LLT::integer(VaListSize * 8));
1836 MIB.buildLoad(Val, MI.getOperand(2),
1839 VaListSize, Align(PtrSize)));
1840 MIB.buildStore(Val, MI.getOperand(1),
1843 VaListSize, Align(PtrSize)));
1844 MI.eraseFromParent();
1845 return true;
1846 }
1847 case Intrinsic::get_dynamic_area_offset: {
1848 MIB.buildConstant(MI.getOperand(0).getReg(), 0);
1849 MI.eraseFromParent();
1850 return true;
1851 }
1852 case Intrinsic::aarch64_mops_memset_tag: {
1853 assert(MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS);
1854 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
1855 // the instruction).
1856 auto &Value = MI.getOperand(3);
1857 Register ExtValueReg = MIB.buildAnyExt(LLT::integer(64), Value).getReg(0);
1858 Value.setReg(ExtValueReg);
1859 return true;
1860 }
1861 case Intrinsic::aarch64_prefetch: {
1862 auto &AddrVal = MI.getOperand(1);
1863
1864 int64_t IsWrite = MI.getOperand(2).getImm();
1865 int64_t Target = MI.getOperand(3).getImm();
1866 int64_t IsStream = MI.getOperand(4).getImm();
1867 int64_t IsData = MI.getOperand(5).getImm();
1868
1869 unsigned PrfOp = (IsWrite << 4) | // Load/Store bit
1870 (!IsData << 3) | // IsDataCache bit
1871 (Target << 1) | // Cache level bits
1872 (unsigned)IsStream; // Stream bit
1873
1874 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
1875 MI.eraseFromParent();
1876 return true;
1877 }
1878 case Intrinsic::aarch64_range_prefetch: {
1879 auto &AddrVal = MI.getOperand(1);
1880
1881 int64_t IsWrite = MI.getOperand(2).getImm();
1882 int64_t IsStream = MI.getOperand(3).getImm();
1883 unsigned PrfOp = (IsStream << 2) | IsWrite;
1884
1885 MIB.buildInstr(AArch64::G_AARCH64_RANGE_PREFETCH)
1886 .addImm(PrfOp)
1887 .add(AddrVal)
1888 .addUse(MI.getOperand(4).getReg()); // Metadata
1889 MI.eraseFromParent();
1890 return true;
1891 }
1892 case Intrinsic::aarch64_prefetch_ir: {
1893 auto &AddrVal = MI.getOperand(1);
1894 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(24).add(AddrVal);
1895 MI.eraseFromParent();
1896 return true;
1897 }
1898 case Intrinsic::aarch64_neon_uaddv:
1899 case Intrinsic::aarch64_neon_saddv:
1900 case Intrinsic::aarch64_neon_umaxv:
1901 case Intrinsic::aarch64_neon_smaxv:
1902 case Intrinsic::aarch64_neon_uminv:
1903 case Intrinsic::aarch64_neon_sminv: {
1904 bool IsSigned = IntrinsicID == Intrinsic::aarch64_neon_saddv ||
1905 IntrinsicID == Intrinsic::aarch64_neon_smaxv ||
1906 IntrinsicID == Intrinsic::aarch64_neon_sminv;
1907
1908 auto OldDst = MI.getOperand(0).getReg();
1909 auto OldDstTy = MRI.getType(OldDst);
1910 LLT NewDstTy = MRI.getType(MI.getOperand(2).getReg()).getElementType();
1911 if (OldDstTy == NewDstTy)
1912 return true;
1913
1914 auto NewDst = MRI.createGenericVirtualRegister(NewDstTy);
1915
1916 Helper.Observer.changingInstr(MI);
1917 MI.getOperand(0).setReg(NewDst);
1918 Helper.Observer.changedInstr(MI);
1919
1920 MIB.setInsertPt(MIB.getMBB(), ++MIB.getInsertPt());
1921 MIB.buildExtOrTrunc(IsSigned ? TargetOpcode::G_SEXT : TargetOpcode::G_ZEXT,
1922 OldDst, NewDst);
1923
1924 return true;
1925 }
1926 case Intrinsic::aarch64_neon_uaddlp:
1927 case Intrinsic::aarch64_neon_saddlp: {
1928 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlp
1929 ? AArch64::G_UADDLP
1930 : AArch64::G_SADDLP;
1931 MIB.buildInstr(Opc, {MI.getOperand(0)}, {MI.getOperand(2)});
1932 MI.eraseFromParent();
1933
1934 return true;
1935 }
1936 case Intrinsic::aarch64_neon_uaddlv:
1937 case Intrinsic::aarch64_neon_saddlv: {
1938 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlv
1939 ? AArch64::G_UADDLV
1940 : AArch64::G_SADDLV;
1941 Register DstReg = MI.getOperand(0).getReg();
1942 Register SrcReg = MI.getOperand(2).getReg();
1943 LLT DstTy = MRI.getType(DstReg);
1944
1945 LLT MidTy, ExtTy;
1946 if (DstTy.isScalar() && DstTy.getScalarSizeInBits() <= 32) {
1947 ExtTy = LLT::integer(32);
1948 MidTy = LLT::fixed_vector(4, ExtTy);
1949 } else {
1950 ExtTy = LLT::integer(64);
1951 MidTy = LLT::fixed_vector(2, ExtTy);
1952 }
1953
1954 Register MidReg =
1955 MIB.buildInstr(Opc, {MidTy}, {SrcReg})->getOperand(0).getReg();
1956 Register ZeroReg =
1957 MIB.buildConstant(LLT::integer(64), 0)->getOperand(0).getReg();
1958 Register ExtReg = MIB.buildInstr(AArch64::G_EXTRACT_VECTOR_ELT, {ExtTy},
1959 {MidReg, ZeroReg})
1960 .getReg(0);
1961
1962 if (DstTy.getScalarSizeInBits() < 32)
1963 MIB.buildTrunc(DstReg, ExtReg);
1964 else
1965 MIB.buildCopy(DstReg, ExtReg);
1966
1967 MI.eraseFromParent();
1968
1969 return true;
1970 }
1971 case Intrinsic::aarch64_neon_smax:
1972 return LowerBinOp(TargetOpcode::G_SMAX);
1973 case Intrinsic::aarch64_neon_smin:
1974 return LowerBinOp(TargetOpcode::G_SMIN);
1975 case Intrinsic::aarch64_neon_umax:
1976 return LowerBinOp(TargetOpcode::G_UMAX);
1977 case Intrinsic::aarch64_neon_umin:
1978 return LowerBinOp(TargetOpcode::G_UMIN);
1979 case Intrinsic::aarch64_neon_fmax:
1980 return LowerBinOp(TargetOpcode::G_FMAXIMUM);
1981 case Intrinsic::aarch64_neon_fmin:
1982 return LowerBinOp(TargetOpcode::G_FMINIMUM);
1983 case Intrinsic::aarch64_neon_fmaxnm:
1984 return LowerBinOp(TargetOpcode::G_FMAXNUM);
1985 case Intrinsic::aarch64_neon_fminnm:
1986 return LowerBinOp(TargetOpcode::G_FMINNUM);
1987 case Intrinsic::aarch64_neon_pmul:
1988 return LowerBinOp(TargetOpcode::G_CLMUL);
1989 case Intrinsic::aarch64_neon_pmull:
1990 case Intrinsic::aarch64_neon_pmull64:
1991 return LowerBinOp(AArch64::G_PMULL);
1992 case Intrinsic::aarch64_neon_smull:
1993 return LowerBinOp(AArch64::G_SMULL);
1994 case Intrinsic::aarch64_neon_umull:
1995 return LowerBinOp(AArch64::G_UMULL);
1996 case Intrinsic::aarch64_neon_sabd:
1997 return LowerBinOp(TargetOpcode::G_ABDS);
1998 case Intrinsic::aarch64_neon_uabd:
1999 return LowerBinOp(TargetOpcode::G_ABDU);
2000 case Intrinsic::aarch64_neon_uhadd:
2001 return LowerBinOp(TargetOpcode::G_UAVGFLOOR);
2002 case Intrinsic::aarch64_neon_urhadd:
2003 return LowerBinOp(TargetOpcode::G_UAVGCEIL);
2004 case Intrinsic::aarch64_neon_shadd:
2005 return LowerBinOp(TargetOpcode::G_SAVGFLOOR);
2006 case Intrinsic::aarch64_neon_srhadd:
2007 return LowerBinOp(TargetOpcode::G_SAVGCEIL);
2008 case Intrinsic::aarch64_neon_sqshrn: {
2009 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2010 return true;
2011 // Create right shift instruction. Store the output register in Shr.
2012 auto Shr = MIB.buildInstr(AArch64::G_VASHR,
2013 {MRI.getType(MI.getOperand(2).getReg())},
2014 {MI.getOperand(2), MI.getOperand(3).getImm()});
2015 // Build the narrow intrinsic, taking in Shr.
2016 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_S, {MI.getOperand(0)}, {Shr});
2017 MI.eraseFromParent();
2018 return true;
2019 }
2020 case Intrinsic::aarch64_neon_sqshrun: {
2021 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2022 return true;
2023 // Create right shift instruction. Store the output register in Shr.
2024 auto Shr = MIB.buildInstr(AArch64::G_VASHR,
2025 {MRI.getType(MI.getOperand(2).getReg())},
2026 {MI.getOperand(2), MI.getOperand(3).getImm()});
2027 // Build the narrow intrinsic, taking in Shr.
2028 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {MI.getOperand(0)}, {Shr});
2029 MI.eraseFromParent();
2030 return true;
2031 }
2032 case Intrinsic::aarch64_neon_sqrshrn: {
2033 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2034 return true;
2035 // Create right shift instruction. Store the output register in Shr.
2036 auto Shr = MIB.buildInstr(AArch64::G_SRSHR_I,
2037 {MRI.getType(MI.getOperand(2).getReg())},
2038 {MI.getOperand(2), MI.getOperand(3).getImm()});
2039 // Build the narrow intrinsic, taking in Shr.
2040 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_S, {MI.getOperand(0)}, {Shr});
2041 MI.eraseFromParent();
2042 return true;
2043 }
2044 case Intrinsic::aarch64_neon_sqrshrun: {
2045 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2046 return true;
2047 // Create right shift instruction. Store the output register in Shr.
2048 auto Shr = MIB.buildInstr(AArch64::G_SRSHR_I,
2049 {MRI.getType(MI.getOperand(2).getReg())},
2050 {MI.getOperand(2), MI.getOperand(3).getImm()});
2051 // Build the narrow intrinsic, taking in Shr.
2052 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {MI.getOperand(0)}, {Shr});
2053 MI.eraseFromParent();
2054 return true;
2055 }
2056 case Intrinsic::aarch64_neon_uqrshrn: {
2057 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2058 return true;
2059 // Create right shift instruction. Store the output register in Shr.
2060 auto Shr = MIB.buildInstr(AArch64::G_URSHR_I,
2061 {MRI.getType(MI.getOperand(2).getReg())},
2062 {MI.getOperand(2), MI.getOperand(3).getImm()});
2063 // Build the narrow intrinsic, taking in Shr.
2064 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2065 MI.eraseFromParent();
2066 return true;
2067 }
2068 case Intrinsic::aarch64_neon_uqshrn: {
2069 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2070 return true;
2071 // Create right shift instruction. Store the output register in Shr.
2072 auto Shr = MIB.buildInstr(AArch64::G_VLSHR,
2073 {MRI.getType(MI.getOperand(2).getReg())},
2074 {MI.getOperand(2), MI.getOperand(3).getImm()});
2075 // Build the narrow intrinsic, taking in Shr.
2076 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2077 MI.eraseFromParent();
2078 return true;
2079 }
2080 case Intrinsic::aarch64_neon_sqshlu: {
2081 // Check if last operand is constant vector dup
2082 auto ShiftAmount =
2083 isConstantOrConstantSplatVector(MI.getOperand(3).getReg(), MRI);
2084 if (ShiftAmount) {
2085 // If so, create a new intrinsic with the correct shift amount
2086 MIB.buildInstr(AArch64::G_SQSHLU_I, {MI.getOperand(0)},
2087 {MI.getOperand(2)})
2088 .addImm(ShiftAmount->getSExtValue());
2089 MI.eraseFromParent();
2090 return true;
2091 }
2092 return false;
2093 }
2094 case Intrinsic::aarch64_neon_vsli: {
2095 MIB.buildInstr(
2096 AArch64::G_SLI, {MI.getOperand(0)},
2097 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2098 MI.eraseFromParent();
2099 break;
2100 }
2101 case Intrinsic::aarch64_neon_vsri: {
2102 MIB.buildInstr(
2103 AArch64::G_SRI, {MI.getOperand(0)},
2104 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2105 MI.eraseFromParent();
2106 break;
2107 }
2108 case Intrinsic::aarch64_neon_abs: {
2109 // Lower the intrinsic to G_ABS.
2110 MIB.buildInstr(TargetOpcode::G_ABS, {MI.getOperand(0)}, {MI.getOperand(2)});
2111 MI.eraseFromParent();
2112 return true;
2113 }
2114 case Intrinsic::aarch64_neon_addhn:
2115 return LowerBinOp(AArch64::G_ADDHN);
2116 case Intrinsic::aarch64_neon_sqadd: {
2117 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2118 return LowerBinOp(TargetOpcode::G_SADDSAT);
2119 break;
2120 }
2121 case Intrinsic::aarch64_neon_sqsub: {
2122 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2123 return LowerBinOp(TargetOpcode::G_SSUBSAT);
2124 break;
2125 }
2126 case Intrinsic::aarch64_neon_uqadd: {
2127 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2128 return LowerBinOp(TargetOpcode::G_UADDSAT);
2129 break;
2130 }
2131 case Intrinsic::aarch64_neon_uqsub: {
2132 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2133 return LowerBinOp(TargetOpcode::G_USUBSAT);
2134 break;
2135 }
2136 case Intrinsic::aarch64_neon_udot:
2137 return LowerTriOp(AArch64::G_UDOT);
2138 case Intrinsic::aarch64_neon_sdot:
2139 return LowerTriOp(AArch64::G_SDOT);
2140 case Intrinsic::aarch64_neon_usdot:
2141 return LowerTriOp(AArch64::G_USDOT);
2142 case Intrinsic::aarch64_neon_sqxtn:
2143 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_S);
2144 case Intrinsic::aarch64_neon_sqxtun:
2145 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_U);
2146 case Intrinsic::aarch64_neon_uqxtn:
2147 return LowerUnaryOp(TargetOpcode::G_TRUNC_USAT_U);
2148 case Intrinsic::aarch64_neon_fcvtzu:
2149 return LowerUnaryOp(TargetOpcode::G_FPTOUI_SAT);
2150 case Intrinsic::aarch64_neon_fcvtzs:
2151 return LowerUnaryOp(TargetOpcode::G_FPTOSI_SAT);
2152 case Intrinsic::aarch64_neon_cls:
2153 return LowerUnaryOp(TargetOpcode::G_CTLS);
2154
2155 case Intrinsic::vector_reverse:
2156 // TODO: Add support for vector_reverse
2157 return false;
2158 }
2159
2160 return true;
2161}
2162
2163bool AArch64LegalizerInfo::legalizeShlAshrLshr(
2165 GISelChangeObserver &Observer) const {
2166 assert(MI.getOpcode() == TargetOpcode::G_ASHR ||
2167 MI.getOpcode() == TargetOpcode::G_LSHR ||
2168 MI.getOpcode() == TargetOpcode::G_SHL);
2169 // If the shift amount is a G_CONSTANT, promote it to a 64 bit type so the
2170 // imported patterns can select it later. Either way, it will be legal.
2171 Register AmtReg = MI.getOperand(2).getReg();
2172 LLT AmtRegEltTy = MRI.getType(AmtReg).getScalarType();
2173 auto VRegAndVal = getIConstantVRegValWithLookThrough(AmtReg, MRI);
2174 if (!VRegAndVal)
2175 return true;
2176 // Check the shift amount is in range for an immediate form.
2177 int64_t Amount = VRegAndVal->Value.getSExtValue();
2178 if (Amount > 31)
2179 return true; // This will have to remain a register variant.
2180 auto ExtCst =
2181 MIRBuilder.buildConstant(AmtRegEltTy.changeElementSize(64), Amount);
2182 Observer.changingInstr(MI);
2183 MI.getOperand(2).setReg(ExtCst.getReg(0));
2184 Observer.changedInstr(MI);
2185 return true;
2186}
2187
2189 MachineRegisterInfo &MRI) {
2190 Base = Root;
2191 Offset = 0;
2192
2193 Register NewBase;
2194 int64_t NewOffset;
2195 if (mi_match(Root, MRI, m_GPtrAdd(m_Reg(NewBase), m_ICst(NewOffset))) &&
2196 isShiftedInt<7, 3>(NewOffset)) {
2197 Base = NewBase;
2198 Offset = NewOffset;
2199 }
2200}
2201
2202// FIXME: This should be removed and replaced with the generic bitcast legalize
2203// action.
2204bool AArch64LegalizerInfo::legalizeLoadStore(
2206 GISelChangeObserver &Observer) const {
2207 assert(MI.getOpcode() == TargetOpcode::G_STORE ||
2208 MI.getOpcode() == TargetOpcode::G_LOAD);
2209 // Here we just try to handle vector loads/stores where our value type might
2210 // have pointer elements, which the SelectionDAG importer can't handle. To
2211 // allow the existing patterns for s64 to fire for p0, we just try to bitcast
2212 // the value to use s64 types.
2213
2214 // Custom legalization requires the instruction, if not deleted, must be fully
2215 // legalized. In order to allow further legalization of the inst, we create
2216 // a new instruction and erase the existing one.
2217
2218 Register ValReg = MI.getOperand(0).getReg();
2219 const LLT ValTy = MRI.getType(ValReg);
2220
2221 if (ValTy == LLT::scalar(128)) {
2222
2223 AtomicOrdering Ordering = (*MI.memoperands_begin())->getSuccessOrdering();
2224 bool IsLoad = MI.getOpcode() == TargetOpcode::G_LOAD;
2225 bool IsLoadAcquire = IsLoad && Ordering == AtomicOrdering::Acquire;
2226 bool IsStoreRelease = !IsLoad && Ordering == AtomicOrdering::Release;
2227 bool IsRcpC3 =
2228 ST->hasLSE2() && ST->hasRCPC3() && (IsLoadAcquire || IsStoreRelease);
2229
2230 LLT s64 = LLT::integer(64);
2231
2232 unsigned Opcode;
2233 if (IsRcpC3) {
2234 Opcode = IsLoad ? AArch64::LDIAPPX : AArch64::STILPX;
2235 } else {
2236 // For LSE2, loads/stores should have been converted to monotonic and had
2237 // a fence inserted after them.
2238 assert(Ordering == AtomicOrdering::Monotonic ||
2239 Ordering == AtomicOrdering::Unordered);
2240 assert(ST->hasLSE2() && "ldp/stp not single copy atomic without +lse2");
2241
2242 Opcode = IsLoad ? AArch64::LDPXi : AArch64::STPXi;
2243 }
2244
2245 MachineInstrBuilder NewI;
2246 if (IsLoad) {
2247 NewI = MIRBuilder.buildInstr(Opcode, {s64, s64}, {});
2248 MIRBuilder.buildMergeLikeInstr(
2249 ValReg, {NewI->getOperand(0), NewI->getOperand(1)});
2250 } else {
2251 auto Split = MIRBuilder.buildUnmerge(s64, MI.getOperand(0));
2252 NewI = MIRBuilder.buildInstr(
2253 Opcode, {}, {Split->getOperand(0), Split->getOperand(1)});
2254 }
2255
2256 if (IsRcpC3) {
2257 NewI.addUse(MI.getOperand(1).getReg());
2258 } else {
2259 Register Base;
2260 int Offset;
2261 matchLDPSTPAddrMode(MI.getOperand(1).getReg(), Base, Offset, MRI);
2262 NewI.addUse(Base);
2263 NewI.addImm(Offset / 8);
2264 }
2265
2266 NewI.cloneMemRefs(MI);
2267 constrainSelectedInstRegOperands(*NewI, *ST->getInstrInfo(),
2268 *MRI.getTargetRegisterInfo(),
2269 *ST->getRegBankInfo());
2270 MI.eraseFromParent();
2271 return true;
2272 }
2273
2274 if (!ValTy.isPointerVector() ||
2275 ValTy.getElementType().getAddressSpace() != 0) {
2276 LLVM_DEBUG(dbgs() << "Tried to do custom legalization on wrong load/store");
2277 return false;
2278 }
2279
2280 unsigned PtrSize = ValTy.getElementType().getSizeInBits();
2281 const LLT NewTy = LLT::vector(ValTy.getElementCount(), LLT::integer(PtrSize));
2282 auto &MMO = **MI.memoperands_begin();
2283 MMO.setType(NewTy);
2284
2285 if (MI.getOpcode() == TargetOpcode::G_STORE) {
2286 auto Bitcast = MIRBuilder.buildBitcast(NewTy, ValReg);
2287 MIRBuilder.buildStore(Bitcast.getReg(0), MI.getOperand(1), MMO);
2288 } else {
2289 auto NewLoad = MIRBuilder.buildLoad(NewTy, MI.getOperand(1), MMO);
2290 MIRBuilder.buildBitcast(ValReg, NewLoad);
2291 }
2292 MI.eraseFromParent();
2293 return true;
2294}
2295
2296bool AArch64LegalizerInfo::legalizeVaArg(MachineInstr &MI,
2298 MachineIRBuilder &MIRBuilder) const {
2299 MachineFunction &MF = MIRBuilder.getMF();
2300 Align Alignment(MI.getOperand(2).getImm());
2301 Register Dst = MI.getOperand(0).getReg();
2302 Register ListPtr = MI.getOperand(1).getReg();
2303
2304 LLT PtrTy = MRI.getType(ListPtr);
2305 LLT IntPtrTy = LLT::integer(PtrTy.getSizeInBits());
2306
2307 const unsigned PtrSize = PtrTy.getSizeInBits() / 8;
2308 const Align PtrAlign = Align(PtrSize);
2309 auto List = MIRBuilder.buildLoad(
2310 PtrTy, ListPtr,
2311 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2312 PtrTy, PtrAlign));
2313
2314 MachineInstrBuilder DstPtr;
2315 if (Alignment > PtrAlign) {
2316 // Realign the list to the actual required alignment.
2317 auto AlignMinus1 =
2318 MIRBuilder.buildConstant(IntPtrTy, Alignment.value() - 1);
2319 auto ListTmp = MIRBuilder.buildPtrAdd(PtrTy, List, AlignMinus1.getReg(0));
2320 DstPtr = MIRBuilder.buildMaskLowPtrBits(PtrTy, ListTmp, Log2(Alignment));
2321 } else
2322 DstPtr = List;
2323
2324 LLT ValTy = MRI.getType(Dst);
2325 uint64_t ValSize = ValTy.getSizeInBits() / 8;
2326 MIRBuilder.buildLoad(
2327 Dst, DstPtr,
2328 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2329 ValTy, std::max(Alignment, PtrAlign)));
2330
2331 auto Size = MIRBuilder.buildConstant(IntPtrTy, alignTo(ValSize, PtrAlign));
2332
2333 auto NewList = MIRBuilder.buildPtrAdd(PtrTy, DstPtr, Size.getReg(0));
2334
2335 MIRBuilder.buildStore(NewList, ListPtr,
2336 *MF.getMachineMemOperand(MachinePointerInfo(),
2338 PtrTy, PtrAlign));
2339
2340 MI.eraseFromParent();
2341 return true;
2342}
2343
2344bool AArch64LegalizerInfo::legalizeBitfieldExtract(
2345 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2346 // Only legal if we can select immediate forms.
2347 // TODO: Lower this otherwise.
2348 return getIConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI) &&
2349 getIConstantVRegValWithLookThrough(MI.getOperand(3).getReg(), MRI);
2350}
2351
2352bool AArch64LegalizerInfo::legalizeCTPOP(MachineInstr &MI,
2354 LegalizerHelper &Helper) const {
2355 // When there is no integer popcount instruction (FEAT_CSSC isn't available),
2356 // it can be more efficiently lowered to the following sequence that uses
2357 // AdvSIMD registers/instructions as long as the copies to/from the AdvSIMD
2358 // registers are cheap.
2359 // FMOV D0, X0 // copy 64-bit int to vector, high bits zero'd
2360 // CNT V0.8B, V0.8B // 8xbyte pop-counts
2361 // ADDV B0, V0.8B // sum 8xbyte pop-counts
2362 // UMOV X0, V0.B[0] // copy byte result back to integer reg
2363 //
2364 // For 128 bit vector popcounts, we lower to the following sequence:
2365 // cnt.16b v0, v0 // v8s16, v4s32, v2s64
2366 // uaddlp.8h v0, v0 // v8s16, v4s32, v2s64
2367 // uaddlp.4s v0, v0 // v4s32, v2s64
2368 // uaddlp.2d v0, v0 // v2s64
2369 //
2370 // For 64 bit vector popcounts, we lower to the following sequence:
2371 // cnt.8b v0, v0 // v4s16, v2s32
2372 // uaddlp.4h v0, v0 // v4s16, v2s32
2373 // uaddlp.2s v0, v0 // v2s32
2374
2375 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2376 Register Dst = MI.getOperand(0).getReg();
2377 Register Val = MI.getOperand(1).getReg();
2378 LLT Ty = MRI.getType(Val);
2379
2380 LLT i64 = LLT::integer(64);
2381 LLT i32 = LLT::integer(32);
2382 LLT i16 = LLT::integer(16);
2383 LLT i8 = LLT::integer(8);
2384 unsigned Size = Ty.getSizeInBits();
2385
2386 assert(Ty == MRI.getType(Dst) &&
2387 "Expected src and dst to have the same type!");
2388
2389 if (ST->hasCSSC() && Ty.isScalar() && Size == 128) {
2390
2391 auto Split = MIRBuilder.buildUnmerge(i64, Val);
2392 auto CTPOP1 = MIRBuilder.buildCTPOP(i64, Split->getOperand(0));
2393 auto CTPOP2 = MIRBuilder.buildCTPOP(i64, Split->getOperand(1));
2394 auto Add = MIRBuilder.buildAdd(i64, CTPOP1, CTPOP2);
2395
2396 MIRBuilder.buildZExt(Dst, Add);
2397 MI.eraseFromParent();
2398 return true;
2399 }
2400
2401 if (!ST->hasNEON() ||
2402 MI.getMF()->getFunction().hasFnAttribute(Attribute::NoImplicitFloat)) {
2403 // Use generic lowering when custom lowering is not possible.
2404 return Ty.isScalar() && (Size == 32 || Size == 64) &&
2405 Helper.lowerBitCount(MI) ==
2407 }
2408
2409 // Pre-conditioning: widen Val up to the nearest vector type.
2410 // s32,s64,v4s16,v2s32 -> v8i8
2411 // v8s16,v4s32,v2s64 -> v16i8
2412 LLT VTy = Size == 128 ? LLT::fixed_vector(16, i8) : LLT::fixed_vector(8, i8);
2413 if (Ty.isScalar()) {
2414 assert((Size == 32 || Size == 64 || Size == 128) && "Expected only 32, 64, or 128 bit scalars!");
2415 if (Size == 32) {
2416 Val = MIRBuilder.buildZExt(i64, Val).getReg(0);
2417 }
2418 }
2419 Val = MIRBuilder.buildBitcast(VTy, Val).getReg(0);
2420
2421 // Count bits in each byte-sized lane.
2422 auto CTPOP = MIRBuilder.buildCTPOP(VTy, Val);
2423
2424 // Sum across lanes.
2425 if (ST->hasDotProd() && Ty.isVector() && Ty.getNumElements() >= 2 &&
2426 Ty.getScalarSizeInBits() != 16) {
2427 LLT Dt = Ty == LLT::fixed_vector(2, i64) ? LLT::fixed_vector(4, i32) : Ty;
2428 auto Zeros = MIRBuilder.buildConstant(Dt, 0);
2429 auto Ones = MIRBuilder.buildConstant(VTy, 1);
2430 MachineInstrBuilder Sum;
2431
2432 if (Ty == LLT::fixed_vector(2, i64)) {
2433 auto UDOT =
2434 MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2435 Sum = MIRBuilder.buildInstr(AArch64::G_UADDLP, {Ty}, {UDOT});
2436 } else if (Ty == LLT::fixed_vector(4, i32)) {
2437 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2438 } else if (Ty == LLT::fixed_vector(2, i32)) {
2439 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2440 } else {
2441 llvm_unreachable("unexpected vector shape");
2442 }
2443
2444 Sum->getOperand(0).setReg(Dst);
2445 MI.eraseFromParent();
2446 return true;
2447 }
2448
2449 Register HSum = CTPOP.getReg(0);
2450 unsigned Opc;
2451 SmallVector<LLT> HAddTys;
2452 if (Ty.isScalar()) {
2453 Opc = Intrinsic::aarch64_neon_uaddlv;
2454 HAddTys.push_back(i32);
2455 } else if (Ty == LLT::fixed_vector(8, i16)) {
2456 Opc = Intrinsic::aarch64_neon_uaddlp;
2457 HAddTys.push_back(LLT::fixed_vector(8, i16));
2458 } else if (Ty == LLT::fixed_vector(4, i32)) {
2459 Opc = Intrinsic::aarch64_neon_uaddlp;
2460 HAddTys.push_back(LLT::fixed_vector(8, i16));
2461 HAddTys.push_back(LLT::fixed_vector(4, i32));
2462 } else if (Ty == LLT::fixed_vector(2, i64)) {
2463 Opc = Intrinsic::aarch64_neon_uaddlp;
2464 HAddTys.push_back(LLT::fixed_vector(8, i16));
2465 HAddTys.push_back(LLT::fixed_vector(4, i32));
2466 HAddTys.push_back(LLT::fixed_vector(2, i64));
2467 } else if (Ty == LLT::fixed_vector(4, i16)) {
2468 Opc = Intrinsic::aarch64_neon_uaddlp;
2469 HAddTys.push_back(LLT::fixed_vector(4, i16));
2470 } else if (Ty == LLT::fixed_vector(2, i32)) {
2471 Opc = Intrinsic::aarch64_neon_uaddlp;
2472 HAddTys.push_back(LLT::fixed_vector(4, i16));
2473 HAddTys.push_back(LLT::fixed_vector(2, i32));
2474 } else
2475 llvm_unreachable("unexpected vector shape");
2477 for (LLT HTy : HAddTys) {
2478 UADD = MIRBuilder.buildIntrinsic(Opc, {HTy}).addUse(HSum);
2479 HSum = UADD.getReg(0);
2480 }
2481
2482 // Post-conditioning.
2483 if (Ty.isScalar() && (Size == 64 || Size == 128))
2484 MIRBuilder.buildZExt(Dst, UADD);
2485 else
2486 UADD->getOperand(0).setReg(Dst);
2487 MI.eraseFromParent();
2488 return true;
2489}
2490
2491bool AArch64LegalizerInfo::legalizeAtomicCmpxchg128(
2492 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2493 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2494 LLT i64 = LLT::integer(64);
2495 auto Addr = MI.getOperand(1).getReg();
2496 auto DesiredI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(2));
2497 auto NewI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(3));
2498 auto DstLo = MRI.createGenericVirtualRegister(i64);
2499 auto DstHi = MRI.createGenericVirtualRegister(i64);
2500
2501 MachineInstrBuilder CAS;
2502 if (ST->hasLSE()) {
2503 // We have 128-bit CASP instructions taking XSeqPair registers, which are
2504 // s128. We need the merge/unmerge to bracket the expansion and pair up with
2505 // the rest of the MIR so we must reassemble the extracted registers into a
2506 // 128-bit known-regclass one with code like this:
2507 //
2508 // %in1 = REG_SEQUENCE Lo, Hi ; One for each input
2509 // %out = CASP %in1, ...
2510 // %OldLo = G_EXTRACT %out, 0
2511 // %OldHi = G_EXTRACT %out, 64
2512 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2513 unsigned Opcode;
2514 switch (Ordering) {
2516 Opcode = AArch64::CASPAX;
2517 break;
2519 Opcode = AArch64::CASPLX;
2520 break;
2523 Opcode = AArch64::CASPALX;
2524 break;
2525 default:
2526 Opcode = AArch64::CASPX;
2527 break;
2528 }
2529
2530 LLT s128 = LLT::integer(128);
2531 auto CASDst = MRI.createGenericVirtualRegister(s128);
2532 auto CASDesired = MRI.createGenericVirtualRegister(s128);
2533 auto CASNew = MRI.createGenericVirtualRegister(s128);
2534 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASDesired}, {})
2535 .addUse(DesiredI->getOperand(0).getReg())
2536 .addImm(AArch64::sube64)
2537 .addUse(DesiredI->getOperand(1).getReg())
2538 .addImm(AArch64::subo64);
2539 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASNew}, {})
2540 .addUse(NewI->getOperand(0).getReg())
2541 .addImm(AArch64::sube64)
2542 .addUse(NewI->getOperand(1).getReg())
2543 .addImm(AArch64::subo64);
2544
2545 CAS = MIRBuilder.buildInstr(Opcode, {CASDst}, {CASDesired, CASNew, Addr});
2546
2547 MIRBuilder.buildExtract({DstLo}, {CASDst}, 0);
2548 MIRBuilder.buildExtract({DstHi}, {CASDst}, 64);
2549 } else {
2550 // The -O0 CMP_SWAP_128 is friendlier to generate code for because LDXP/STXP
2551 // can take arbitrary registers so it just has the normal GPR64 operands the
2552 // rest of AArch64 is expecting.
2553 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2554 unsigned Opcode;
2555 switch (Ordering) {
2557 Opcode = AArch64::CMP_SWAP_128_ACQUIRE;
2558 break;
2560 Opcode = AArch64::CMP_SWAP_128_RELEASE;
2561 break;
2564 Opcode = AArch64::CMP_SWAP_128;
2565 break;
2566 default:
2567 Opcode = AArch64::CMP_SWAP_128_MONOTONIC;
2568 break;
2569 }
2570
2571 auto Scratch = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
2572 CAS = MIRBuilder.buildInstr(Opcode, {DstLo, DstHi, Scratch},
2573 {Addr, DesiredI->getOperand(0),
2574 DesiredI->getOperand(1), NewI->getOperand(0),
2575 NewI->getOperand(1)});
2576 }
2577
2578 CAS.cloneMemRefs(MI);
2579 constrainSelectedInstRegOperands(*CAS, *ST->getInstrInfo(),
2580 *MRI.getTargetRegisterInfo(),
2581 *ST->getRegBankInfo());
2582
2583 MIRBuilder.buildMergeLikeInstr(MI.getOperand(0), {DstLo, DstHi});
2584 MI.eraseFromParent();
2585 return true;
2586}
2587
2588bool AArch64LegalizerInfo::legalizeCTTZ(MachineInstr &MI,
2589 LegalizerHelper &Helper) const {
2590 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2591 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2592 LLT Ty = MRI.getType(MI.getOperand(1).getReg());
2593 auto BitReverse = MIRBuilder.buildBitReverse(Ty, MI.getOperand(1));
2594 MIRBuilder.buildCTLZ(MI.getOperand(0).getReg(), BitReverse);
2595 MI.eraseFromParent();
2596 return true;
2597}
2598
2599bool AArch64LegalizerInfo::legalizeMemOps(MachineInstr &MI,
2600 LegalizerHelper &Helper) const {
2601 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2602
2603 // Tagged version MOPSMemorySetTagged is legalised in legalizeIntrinsic
2604 if (MI.getOpcode() == TargetOpcode::G_MEMSET ||
2605 MI.getOpcode() == TargetOpcode::G_MEMSET_INLINE) {
2606 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
2607 // the instruction).
2608 auto &Value = MI.getOperand(1);
2609 Register ExtValueReg =
2610 MIRBuilder.buildAnyExt(LLT::integer(64), Value).getReg(0);
2611 Value.setReg(ExtValueReg);
2612 return true;
2613 }
2614
2615 return false;
2616}
2617
2618bool AArch64LegalizerInfo::legalizeExtractVectorElt(
2619 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2620 const GExtractVectorElement *Element = cast<GExtractVectorElement>(&MI);
2621 auto VRegAndVal =
2623 if (VRegAndVal)
2624 return true;
2625 LLT VecTy = MRI.getType(Element->getVectorReg());
2626 if (VecTy.isScalableVector())
2627 return true;
2628 return Helper.lowerExtractInsertVectorElt(MI) !=
2630}
2631
2632bool AArch64LegalizerInfo::legalizeDynStackAlloc(
2633 MachineInstr &MI, LegalizerHelper &Helper) const {
2634 MachineFunction &MF = *MI.getParent()->getParent();
2635 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2636 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2637
2638 // If stack probing is not enabled for this function, use the default
2639 // lowering.
2640 if (!MF.getFunction().hasFnAttribute("probe-stack") ||
2641 MF.getFunction().getFnAttribute("probe-stack").getValueAsString() !=
2642 "inline-asm") {
2643 Helper.lowerDynStackAlloc(MI);
2644 return true;
2645 }
2646
2647 Register Dst = MI.getOperand(0).getReg();
2648 Register AllocSize = MI.getOperand(1).getReg();
2649 Align Alignment = assumeAligned(MI.getOperand(2).getImm());
2650
2651 assert(MRI.getType(Dst) == LLT::pointer(0, 64) &&
2652 "Unexpected type for dynamic alloca");
2653 assert(MRI.getType(AllocSize) == LLT::scalar(64) &&
2654 "Unexpected type for dynamic alloca");
2655
2656 LLT PtrTy = MRI.getType(Dst);
2657 Register SPReg =
2659 Register SPTmp =
2660 Helper.getDynStackAllocTargetPtr(SPReg, AllocSize, Alignment, PtrTy);
2661 auto NewMI =
2662 MIRBuilder.buildInstr(AArch64::PROBED_STACKALLOC_DYN, {}, {SPTmp});
2663 MRI.setRegClass(NewMI.getReg(0), &AArch64::GPR64commonRegClass);
2664 MIRBuilder.setInsertPt(*NewMI->getParent(), NewMI);
2665 MIRBuilder.buildCopy(Dst, SPTmp);
2666
2667 MI.eraseFromParent();
2668 return true;
2669}
2670
2671bool AArch64LegalizerInfo::legalizePrefetch(MachineInstr &MI,
2672 LegalizerHelper &Helper) const {
2673 MachineIRBuilder &MIB = Helper.MIRBuilder;
2674 auto &AddrVal = MI.getOperand(0);
2675
2676 int64_t IsWrite = MI.getOperand(1).getImm();
2677 int64_t Locality = MI.getOperand(2).getImm();
2678 int64_t IsData = MI.getOperand(3).getImm();
2679
2680 bool IsStream = Locality == 0;
2681 if (Locality != 0) {
2682 assert(Locality <= 3 && "Prefetch locality out-of-range");
2683 // The locality degree is the opposite of the cache speed.
2684 // Put the number the other way around.
2685 // The encoding starts at 0 for level 1
2686 Locality = 3 - Locality;
2687 }
2688
2689 unsigned PrfOp = (IsWrite << 4) | (!IsData << 3) | (Locality << 1) | IsStream;
2690
2691 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
2692 MI.eraseFromParent();
2693 return true;
2694}
2695
2696bool AArch64LegalizerInfo::legalizeConcatVectors(
2698 MachineIRBuilder &MIRBuilder) const {
2699 // Widen sub-byte element vectors to byte-sized elements before concatenating.
2700 // This is analogous to SDAG's integer type promotion for sub-byte types.
2702 Register DstReg = Concat.getReg(0);
2703 LLT DstTy = MRI.getType(DstReg);
2704 assert(DstTy.getScalarSizeInBits() < 8 && "Expected dst ty to be < 8b");
2705
2706 unsigned WideEltSize =
2707 std::max(8u, (unsigned)PowerOf2Ceil(DstTy.getScalarSizeInBits()));
2708 LLT SrcTy = MRI.getType(Concat.getSourceReg(0));
2709 LLT WideSrcTy = SrcTy.changeElementSize(WideEltSize);
2710 LLT WideDstTy = DstTy.changeElementSize(WideEltSize);
2711
2712 SmallVector<Register> WideSrcs;
2713 for (unsigned I = 0; I < Concat.getNumSources(); ++I) {
2714 auto Wide = MIRBuilder.buildAnyExt(WideSrcTy, Concat.getSourceReg(I));
2715 WideSrcs.push_back(Wide.getReg(0));
2716 }
2717
2718 auto WideConcat = MIRBuilder.buildConcatVectors(WideDstTy, WideSrcs);
2719 MIRBuilder.buildTrunc(DstReg, WideConcat);
2720 MI.eraseFromParent();
2721 return true;
2722}
2723
2724bool AArch64LegalizerInfo::legalizeFptrunc(MachineInstr &MI,
2725 MachineIRBuilder &MIRBuilder,
2726 MachineRegisterInfo &MRI) const {
2727 auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
2728
2729 // This function legalizes f64 -> bf16 and f64 -> f16 truncations via f64 ->
2730 // f32 G_FPTRUNC_ODD and f32 -> [b]f16 G_FPTRUNC, which apparently avoids the
2731 // usual double-rounding issue that could be present from using twin
2732 // G_FPTRUNC.
2733
2734 if (DstTy.isBFloat16() && SrcTy.isFloat64()) {
2735 auto Mid = MIRBuilder.buildInstr(AArch64::G_FPTRUNC_ODD, {LLT::float32()},
2736 {Src}, MI.getFlags());
2737 MIRBuilder.buildInstr(AArch64::G_FPTRUNC, {Dst}, {Mid}, MI.getFlags());
2738 MI.eraseFromParent();
2739 return true;
2740 }
2741
2742 assert(SrcTy.isFixedVector() && isPowerOf2_32(SrcTy.getNumElements()) &&
2743 "Expected a power of 2 elements");
2744
2745 // We must mutate types here as FPTrunc may be used on a IEEE floating point
2746 // or a brainfloat.
2747 LLT v2s16 = DstTy.changeElementCount(2);
2748 LLT v4s16 = DstTy.changeElementCount(4);
2749 LLT v2s32 = SrcTy.changeElementCount(2).changeElementSize(32);
2750 LLT v4s32 = SrcTy.changeElementCount(4).changeElementSize(32);
2751 LLT v2s64 = SrcTy.changeElementCount(2);
2752
2753 SmallVector<Register> RegsToUnmergeTo;
2754 SmallVector<Register> TruncOddDstRegs;
2755 SmallVector<Register> RegsToMerge;
2756
2757 unsigned ElemCount = SrcTy.getNumElements();
2758
2759 // Find the biggest size chunks we can work with
2760 int StepSize = ElemCount % 4 ? 2 : 4;
2761
2762 // If we have a power of 2 greater than 2, we need to first unmerge into
2763 // enough pieces
2764 if (ElemCount <= 2)
2765 RegsToUnmergeTo.push_back(Src);
2766 else {
2767 for (unsigned i = 0; i < ElemCount / 2; ++i)
2768 RegsToUnmergeTo.push_back(MRI.createGenericVirtualRegister(v2s64));
2769
2770 MIRBuilder.buildUnmerge(RegsToUnmergeTo, Src);
2771 }
2772
2773 // Create all of the round-to-odd instructions and store them
2774 for (auto SrcReg : RegsToUnmergeTo) {
2775 Register Mid = MIRBuilder
2776 .buildInstr(AArch64::G_FPTRUNC_ODD, {v2s32}, {SrcReg},
2777 MI.getFlags())
2778 .getReg(0);
2779 TruncOddDstRegs.push_back(Mid);
2780 }
2781
2782 // Truncate 4s32 to 4s16 if we can to reduce instruction count, otherwise
2783 // truncate 2s32 to 2s16.
2784 unsigned Index = 0;
2785 for (unsigned LoopIter = 0; LoopIter < ElemCount / StepSize; ++LoopIter) {
2786 if (StepSize == 4) {
2787 Register ConcatDst =
2788 MIRBuilder
2790 {v4s32}, {TruncOddDstRegs[Index++], TruncOddDstRegs[Index++]})
2791 .getReg(0);
2792
2793 RegsToMerge.push_back(
2794 MIRBuilder.buildFPTrunc(v4s16, ConcatDst, MI.getFlags()).getReg(0));
2795 } else {
2796 RegsToMerge.push_back(
2797 MIRBuilder
2798 .buildFPTrunc(v2s16, TruncOddDstRegs[Index++], MI.getFlags())
2799 .getReg(0));
2800 }
2801 }
2802
2803 // If there is only one register, replace the destination
2804 if (RegsToMerge.size() == 1) {
2805 MRI.replaceRegWith(Dst, RegsToMerge.pop_back_val());
2806 MI.eraseFromParent();
2807 return true;
2808 }
2809
2810 // Merge the rest of the instructions & replace the register
2811 Register Fin = MIRBuilder.buildMergeLikeInstr(DstTy, RegsToMerge).getReg(0);
2812 MRI.replaceRegWith(Dst, Fin);
2813 MI.eraseFromParent();
2814 return true;
2815}
2816
2817bool AArch64LegalizerInfo::legalizeGetRounding(MachineInstr &MI,
2818 MachineIRBuilder &MIRBuilder,
2820 LegalizerHelper &Helper) const {
2821 const LLT I32 = LLT::integer(32);
2822 const LLT I64 = LLT::integer(64);
2823
2824 Register Dst = MI.getOperand(0).getReg();
2825 Register FPCR64 = MRI.createGenericVirtualRegister(I64);
2826 MachineInstrBuilder GetFPCR =
2827 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, ArrayRef{FPCR64});
2828
2829 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2830 // 3->0, so we add one to the FPCR bits for the rounding mode.
2831 // Instead of shifting and then adding as `((FPCR >> 22) + 1) & 0b11` which
2832 // generates 3 instructions, we increment the rounding mode with
2833 // `(FPCR + (1 << 22))` and extract the bits. The shift and addition is done
2834 // in one instruction as `add .., .., #1024, lsl #12`, so overall we generate
2835 // one less instruction.
2836 auto FPCR32 = MIRBuilder.buildTrunc(I32, GetFPCR);
2837 auto One = MIRBuilder.buildConstant(I32, 1U << 22);
2838 auto Added = MIRBuilder.buildAdd(I32, FPCR32, One);
2839 auto LSB = MIRBuilder.buildConstant(I32, 22);
2840 auto Width = MIRBuilder.buildConstant(I32, 2);
2841 MIRBuilder.buildInstr(TargetOpcode::G_UBFX, {Dst}, {Added, LSB, Width});
2842
2843 MI.eraseFromParent();
2844 return true;
2845}
2846
2847bool AArch64LegalizerInfo::legalizeSetRounding(MachineInstr &MI,
2848 MachineIRBuilder &MIRBuilder,
2850 LegalizerHelper &Helper) const {
2851 const LLT I32 = LLT::integer(32);
2852 const LLT I64 = LLT::integer(64);
2853
2854 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2855 // 3->0, so calculate the new value of FPCR[23:22] as `((arg - 1) & 3) << 22`.
2856 Register RM = MI.getOperand(0).getReg();
2857 auto One = MIRBuilder.buildConstant(I32, 1);
2858 auto Subtracted = MIRBuilder.buildSub(I32, RM, One);
2859 auto Mask = MIRBuilder.buildConstant(I32, 0b11);
2860 auto Masked = MIRBuilder.buildAnd(I32, Subtracted, Mask);
2861 auto ShiftAmount = MIRBuilder.buildConstant(I32, 22);
2862 auto Shifted = MIRBuilder.buildShl(I32, Masked, ShiftAmount);
2863
2864 // Get current value of FPCR.
2865 MachineInstrBuilder GetFPCR =
2866 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, {I64});
2867
2868 // (FPCR & ~Mask) | Shifted
2869 auto FPCRMask = MIRBuilder.buildConstant(I64, ~((int64_t)0b11 << 22));
2870 auto FPCRMasked = MIRBuilder.buildAnd(I64, GetFPCR, FPCRMask);
2871 auto ShiftedS64 = MIRBuilder.buildZExt(I64, Shifted);
2872 auto FPCRUpdated = MIRBuilder.buildOr(I64, FPCRMasked, ShiftedS64);
2873
2874 // Write new FPCR.
2875 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_set_fpcr, ArrayRef<Register>())
2876 .addUse(FPCRUpdated.getReg(0));
2877
2878 MI.eraseFromParent();
2879
2880 return true;
2881}
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:79
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:1057
LLVM_ABI APInt urem(const APInt &RHS) const
Unsigned remainder operation.
Definition APInt.cpp:1695
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:765
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition Function.cpp:730
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:577
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,...