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