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 // The range of a fp16 value fits into an i17, so we can lower the width
987 // to i64.
989 [=](const LegalityQuery &Query) {
990 return Query.Types[1] == f16 && Query.Types[0].getSizeInBits() > 64;
991 },
992 changeTo(0, i64))
993 .lowerIf(::any(scalarWiderThan(0, 64), scalarWiderThan(1, 64)), 0)
995 .widenScalarToNextPow2(0, /*MinSize=*/32)
996 .minScalar(0, s32)
998 [HasFP16](const LegalityQuery &Query) {
999 return (!HasFP16 && Query.Types[1].getScalarType().isFloat16()) ||
1000 Query.Types[1].getScalarType().isBFloat16();
1001 },
1002 changeElementTo(1, f32))
1003 .widenScalarIf(
1004 [=](const LegalityQuery &Query) {
1005 unsigned ITySize = Query.Types[0].getScalarSizeInBits();
1006 return (ITySize == 16 || ITySize == 32 || ITySize == 64) &&
1007 ITySize > Query.Types[1].getScalarSizeInBits();
1008 },
1010 .widenScalarIf(
1011 [=](const LegalityQuery &Query) {
1012 unsigned FTySize = Query.Types[1].getScalarSizeInBits();
1013 return (FTySize == 16 || FTySize == 32 || FTySize == 64) &&
1014 Query.Types[0].getScalarSizeInBits() < FTySize;
1015 },
1018 .clampNumElements(0, v4s16, v8s16)
1019 .clampNumElements(0, v2s32, v4s32)
1020 .clampMaxNumElements(0, s64, 2);
1021
1022 getActionDefinitionsBuilder({G_SITOFP, G_UITOFP})
1023 .legalFor({{f32, i32},
1024 {f64, i32},
1025 {f32, i64},
1026 {f64, i64},
1027 {v2f32, v2i32},
1028 {v4f32, v4i32},
1029 {v2f64, v2i64}})
1030 .legalFor(HasFP16,
1031 {{f16, i32}, {f16, i64}, {v4f16, v4i16}, {v8f16, v8i16}})
1032 .unsupportedIf([&](const LegalityQuery &Query) {
1033 return Query.Types[0].getScalarType().isBFloat16();
1034 })
1035 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1)
1039 .minScalar(1, f32)
1040 .lowerIf([](const LegalityQuery &Query) {
1041 return Query.Types[1].isVector() &&
1042 Query.Types[1].getScalarSizeInBits() == 64 &&
1043 Query.Types[0].getScalarSizeInBits() == 16;
1044 })
1045 .widenScalarOrEltToNextPow2OrMinSize(0, /*MinSize=*/HasFP16 ? 16 : 32)
1046 .scalarizeIf(
1047 // v2i64->v2f32 needs to scalarize to avoid double-rounding issues.
1048 [](const LegalityQuery &Query) {
1049 return Query.Types[0].getScalarSizeInBits() == 32 &&
1050 Query.Types[1].getScalarSizeInBits() == 64;
1051 },
1052 0)
1053 .widenScalarIf(
1054 [](const LegalityQuery &Query) {
1055 return Query.Types[1].getScalarSizeInBits() <= 64 &&
1056 Query.Types[0].getScalarSizeInBits() <
1057 Query.Types[1].getScalarSizeInBits();
1058 },
1060 .widenScalarIf(
1061 [](const LegalityQuery &Query) {
1062 return Query.Types[0].getScalarSizeInBits() <= 64 &&
1063 Query.Types[0].getScalarSizeInBits() >
1064 Query.Types[1].getScalarSizeInBits();
1065 },
1067 .clampNumElements(0, v4s16, v8s16)
1068 .clampNumElements(0, v2s32, v4s32)
1069 .clampMaxNumElements(0, s64, 2)
1070 .libcallFor({{f16, i128},
1071 {f32, i128},
1072 {f64, i128},
1073 {f128, i128},
1074 {f128, i32},
1075 {f128, i64}});
1076
1077 // Control-flow
1078 getActionDefinitionsBuilder(G_BR).alwaysLegal();
1079 getActionDefinitionsBuilder(G_BRCOND)
1080 .legalFor({s32})
1081 .clampScalar(0, s32, s32);
1082 getActionDefinitionsBuilder(G_BRINDIRECT).legalFor({p0});
1083
1084 getActionDefinitionsBuilder(G_SELECT)
1085 .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
1086 .widenScalarToNextPow2(0)
1087 .clampScalar(0, s32, s64)
1088 .clampScalar(1, s32, s32)
1091 .lowerIf(isVector(0));
1092
1093 // Pointer-handling
1094 getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
1095
1096 if (TM.getCodeModel() == CodeModel::Small)
1097 getActionDefinitionsBuilder(G_GLOBAL_VALUE).custom();
1098 else
1099 getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0});
1100
1101 getActionDefinitionsBuilder(G_PTRAUTH_GLOBAL_VALUE)
1102 .legalIf(all(typeIs(0, p0), typeIs(1, p0)));
1103
1104 getActionDefinitionsBuilder(G_PTRTOINT)
1105 .legalFor({{i64, p0}, {v2i64, v2p0}})
1106 .widenScalarToNextPow2(0, 64)
1107 .clampScalar(0, s64, s64)
1108 .clampMaxNumElements(0, s64, 2);
1109
1110 getActionDefinitionsBuilder(G_INTTOPTR)
1111 .unsupportedIf([&](const LegalityQuery &Query) {
1112 return Query.Types[0].getSizeInBits() != Query.Types[1].getSizeInBits();
1113 })
1114 .legalFor({{p0, i64}, {v2p0, v2i64}})
1115 .clampMaxNumElements(1, s64, 2);
1116
1117 // Casts for 32 and 64-bit width type are just copies.
1118 // Same for 128-bit width type, except they are on the FPR bank.
1119 getActionDefinitionsBuilder(G_BITCAST)
1121 // Keeping 32-bit instructions legal to prevent regression in some tests
1122 .legalForCartesianProduct({s32, v2s16, v4s8})
1123 .legalForCartesianProduct({s64, v8s8, v4s16, v2s32})
1124 .legalForCartesianProduct({s128, v16s8, v8s16, v4s32, v2s64, v2p0})
1125 .customIf([=](const LegalityQuery &Query) {
1126 // Handle casts from i1 vectors to scalars.
1127 LLT DstTy = Query.Types[0];
1128 LLT SrcTy = Query.Types[1];
1129 return DstTy.isScalar() && SrcTy.isVector() &&
1130 SrcTy.getScalarSizeInBits() == 1;
1131 })
1132 .lowerIf([=](const LegalityQuery &Query) {
1133 return Query.Types[0].isVector() != Query.Types[1].isVector();
1134 })
1135 // moreElementsToNextPow2 cannot pad the source to match, so lower
1136 .lowerIf([=](const LegalityQuery &Query) {
1137 LLT DstTy = Query.Types[0];
1138 LLT SrcTy = Query.Types[1];
1139 if (!DstTy.isFixedVector() || !SrcTy.isFixedVector())
1140 return false;
1141 unsigned MoreElts = 1u << Log2_32_Ceil(DstTy.getNumElements());
1142 return SrcTy.getNumElements() * MoreElts % DstTy.getNumElements() != 0;
1143 })
1145 .clampNumElements(0, v8s8, v16s8)
1146 .clampNumElements(0, v4s16, v8s16)
1147 .clampNumElements(0, v2s32, v4s32)
1148 .clampMaxNumElements(0, s64, 2)
1149 .lower();
1150
1151 getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
1152
1153 // va_list must be a pointer, but most sized types are pretty easy to handle
1154 // as the destination.
1155 getActionDefinitionsBuilder(G_VAARG)
1156 .customForCartesianProduct({s8, s16, s32, s64, p0}, {p0})
1157 .clampScalar(0, s8, s64)
1158 .widenScalarToNextPow2(0, /*Min*/ 8);
1159
1160 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG_WITH_SUCCESS)
1161 .lowerIf(
1162 all(typeInSet(0, {s8, s16, s32, s64, s128}), typeIs(2, p0)));
1163
1164 bool UseOutlineAtomics = ST.outlineAtomics() && !ST.hasLSE();
1165
1166 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG)
1167 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1168 .customFor(!UseOutlineAtomics, {{s128, p0}})
1169 .libcallFor(UseOutlineAtomics,
1170 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}, {s128, p0}})
1171 .clampScalar(0, s32, s64);
1172
1173 getActionDefinitionsBuilder({G_ATOMICRMW_XCHG, G_ATOMICRMW_ADD,
1174 G_ATOMICRMW_SUB, G_ATOMICRMW_AND, G_ATOMICRMW_OR,
1175 G_ATOMICRMW_XOR})
1176 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1177 .libcallFor(UseOutlineAtomics,
1178 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}})
1179 .clampScalar(0, s32, s64);
1180
1181 // Do not outline these atomics operations, as per comment in
1182 // AArch64ISelLowering.cpp's shouldExpandAtomicRMWInIR().
1183 getActionDefinitionsBuilder(
1184 {G_ATOMICRMW_MIN, G_ATOMICRMW_MAX, G_ATOMICRMW_UMIN, G_ATOMICRMW_UMAX})
1185 .legalIf(all(typeInSet(0, {s32, s64}), typeIs(1, p0)))
1186 .clampScalar(0, s32, s64);
1187
1188 getActionDefinitionsBuilder(G_BLOCK_ADDR).legalFor({p0});
1189
1190 // Merge/Unmerge
1191 for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) {
1192 unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1;
1193 unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0;
1194 getActionDefinitionsBuilder(Op)
1195 .widenScalarToNextPow2(LitTyIdx, 8)
1196 // Above s64 lowered shifts narrow back to a merge and never terminate
1197 .lowerIf([=](const LegalityQuery &Q) {
1198 const LLT BigTy = Q.Types[BigTyIdx];
1199 return BigTy.isScalar() && !isPowerOf2_32(BigTy.getSizeInBits()) &&
1200 BigTy.getSizeInBits() < 64;
1201 })
1202 .widenScalarToNextPow2(BigTyIdx, 32)
1203 .clampScalar(LitTyIdx, s8, s64)
1204 .clampScalar(BigTyIdx, s32, s128)
1205 .legalIf([=](const LegalityQuery &Q) {
1206 switch (Q.Types[BigTyIdx].getSizeInBits()) {
1207 case 32:
1208 case 64:
1209 case 128:
1210 break;
1211 default:
1212 return false;
1213 }
1214 switch (Q.Types[LitTyIdx].getSizeInBits()) {
1215 case 8:
1216 case 16:
1217 case 32:
1218 case 64:
1219 return true;
1220 default:
1221 return false;
1222 }
1223 });
1224 }
1225
1226 // TODO : nxv4s16, nxv2s16, nxv2s32
1227 getActionDefinitionsBuilder(G_EXTRACT_VECTOR_ELT)
1228 .legalFor(HasSVE, {{s16, nxv16s8, s64},
1229 {s16, nxv8s16, s64},
1230 {s32, nxv4s32, s64},
1231 {s64, nxv2s64, s64}})
1232 .unsupportedIf([=](const LegalityQuery &Query) {
1233 const LLT &EltTy = Query.Types[1].getElementType();
1234 if (Query.Types[1].isScalableVector())
1235 return false;
1236 return Query.Types[0] != EltTy;
1237 })
1238 .minScalar(2, s64)
1239 .customIf([=](const LegalityQuery &Query) {
1240 const LLT &VecTy = Query.Types[1];
1241 return VecTy == v8s8 || VecTy == v16s8 || VecTy == v2s16 ||
1242 VecTy == v4s16 || VecTy == v8s16 || VecTy == v2s32 ||
1243 VecTy == v4s32 || VecTy == v2s64 || VecTy == v2p0;
1244 })
1245 .minScalarOrEltIf(
1246 [=](const LegalityQuery &Query) {
1247 // We want to promote to <M x s1> to <M x s64> if that wouldn't
1248 // cause the total vec size to be > 128b.
1249 return Query.Types[1].isFixedVector() &&
1250 Query.Types[1].getNumElements() <= 2;
1251 },
1252 0, s64)
1253 .minScalarOrEltIf(
1254 [=](const LegalityQuery &Query) {
1255 return Query.Types[1].isFixedVector() &&
1256 Query.Types[1].getNumElements() <= 4;
1257 },
1258 0, s32)
1259 .minScalarOrEltIf(
1260 [=](const LegalityQuery &Query) {
1261 return Query.Types[1].isFixedVector() &&
1262 Query.Types[1].getNumElements() <= 8;
1263 },
1264 0, s16)
1265 .minScalarOrEltIf(
1266 [=](const LegalityQuery &Query) {
1267 return Query.Types[1].isFixedVector() &&
1268 Query.Types[1].getNumElements() <= 16;
1269 },
1270 0, s8)
1271 .minScalarOrElt(0, s8) // Worst case, we need at least s8.
1272 .moreElementsToNextPow2(1)
1273 .clampMaxNumElements(1, s64, 2)
1274 .clampMaxNumElements(1, s32, 4)
1275 .clampMaxNumElements(1, s16, 8)
1276 .clampMaxNumElements(1, s8, 16)
1277 .clampMaxNumElements(1, p0, 2)
1278 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1);
1279
1280 getActionDefinitionsBuilder(G_INSERT_VECTOR_ELT)
1281 .legalIf(
1282 typeInSet(0, {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64, v2p0}))
1283 .legalFor(HasSVE, {{nxv16s8, s32, s64},
1284 {nxv8s16, s32, s64},
1285 {nxv4s32, s32, s64},
1286 {nxv2s64, s64, s64}})
1288 .widenVectorEltsToVectorMinSize(0, 64)
1289 .clampNumElements(0, v8s8, v16s8)
1290 .clampNumElements(0, v4s16, v8s16)
1291 .clampNumElements(0, v2s32, v4s32)
1292 .clampMaxNumElements(0, s64, 2)
1293 .clampMaxNumElements(0, p0, 2)
1294 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
1295
1296 getActionDefinitionsBuilder(G_BUILD_VECTOR)
1297 .legalFor({{v8s8, s8},
1298 {v16s8, s8},
1299 {v4s16, s16},
1300 {v8s16, s16},
1301 {v2s32, s32},
1302 {v4s32, s32},
1303 {v2s64, s64},
1304 {v2p0, p0}})
1305 .clampNumElements(0, v4s32, v4s32)
1306 .clampNumElements(0, v2s64, v2s64)
1307 .minScalarOrElt(0, s8)
1308 .widenVectorEltsToVectorMinSize(0, 64)
1309 .widenScalarOrEltToNextPow2(0)
1310 .minScalarSameAs(1, 0);
1311
1312 getActionDefinitionsBuilder(G_BUILD_VECTOR_TRUNC).lower();
1313
1314 getActionDefinitionsBuilder(G_SPLAT_VECTOR)
1315 .legalFor({{v8s8, s8},
1316 {v8s8, s32},
1317 {v16s8, s8},
1318 {v16s8, s32},
1319 {v4s16, s16},
1320 {v4s16, s32},
1321 {v8s16, s16},
1322 {v8s16, s32},
1323 {v2s32, s32},
1324 {v4s32, s32},
1325 {v2s64, s64},
1326 {v2p0, p0}});
1327
1328 getActionDefinitionsBuilder(G_SHUFFLE_VECTOR)
1329 .legalIf([=](const LegalityQuery &Query) {
1330 const LLT &DstTy = Query.Types[0];
1331 const LLT &SrcTy = Query.Types[1];
1332 // For now just support the TBL2 variant which needs the source vectors
1333 // to be the same size as the dest.
1334 if (DstTy != SrcTy)
1335 return false;
1336 return llvm::is_contained(
1337 {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64}, DstTy);
1338 })
1339 .moreElementsIf(
1340 [](const LegalityQuery &Query) {
1341 return Query.Types[0].getNumElements() >
1342 Query.Types[1].getNumElements();
1343 },
1344 changeTo(1, 0))
1346 .moreElementsIf(
1347 [](const LegalityQuery &Query) {
1348 return Query.Types[0].getNumElements() <
1349 Query.Types[1].getNumElements();
1350 },
1351 changeTo(0, 1))
1352 .widenScalarOrEltToNextPow2OrMinSize(0, 8)
1353 .clampNumElements(0, v8s8, v16s8)
1354 .clampNumElements(0, v4s16, v8s16)
1355 .clampNumElements(0, v4s32, v4s32)
1356 .clampNumElements(0, v2s64, v2s64)
1357 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
1358 .bitcastIf(isPointerVector(0), [=](const LegalityQuery &Query) {
1359 // Bitcast pointers vector to i64.
1360 const LLT DstTy = Query.Types[0];
1361 return std::pair(
1362 0, LLT::vector(DstTy.getElementCount(), LLT::integer(64)));
1363 });
1364
1365 getActionDefinitionsBuilder(G_CONCAT_VECTORS)
1366 .legalFor({{v16s8, v8s8}, {v8s16, v4s16}, {v4s32, v2s32}})
1367 .customIf([=](const LegalityQuery &Query) {
1368 return Query.Types[0].isFixedVector() &&
1369 Query.Types[0].getScalarSizeInBits() < 8;
1370 })
1371 .bitcastIf(
1372 [=](const LegalityQuery &Query) {
1373 return Query.Types[0].isFixedVector() &&
1374 Query.Types[1].isFixedVector() &&
1375 Query.Types[0].getScalarSizeInBits() >= 8 &&
1376 isPowerOf2_64(Query.Types[0].getScalarSizeInBits()) &&
1377 Query.Types[0].getSizeInBits() <= 128 &&
1378 Query.Types[1].getSizeInBits() <= 64;
1379 },
1380 [=](const LegalityQuery &Query) {
1381 const LLT DstTy = Query.Types[0];
1382 const LLT SrcTy = Query.Types[1];
1383 return std::pair(
1384 0, DstTy.changeElementSize(SrcTy.getSizeInBits())
1387 SrcTy.getNumElements())));
1388 });
1389
1390 getActionDefinitionsBuilder(G_EXTRACT_SUBVECTOR)
1391 .legalFor({{v8s8, v16s8}, {v4s16, v8s16}, {v2s32, v4s32}})
1393 .clampMaxNumElements(0, s8, 16)
1394 .clampMaxNumElements(0, s16, 8)
1395 .clampMaxNumElements(0, s32, 4)
1396 .clampNumElements(1, v8s8, v16s8)
1397 .clampNumElements(1, v4s16, v8s16)
1398 .clampNumElements(1, v2s32, v4s32)
1399 .lower()
1400 .immIdx(0); // Inform verifier imm idx 0 is handled.
1401
1402 // TODO: {nxv16s8, s8}, {nxv8s16, s16}
1403 getActionDefinitionsBuilder(G_SPLAT_VECTOR)
1404 .legalFor(HasSVE, {{nxv4s32, s32}, {nxv2s64, s64}});
1405
1406 getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
1407
1408 getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, s64}});
1409
1410 getActionDefinitionsBuilder({G_TRAP, G_DEBUGTRAP, G_UBSANTRAP}).alwaysLegal();
1411
1412 getActionDefinitionsBuilder(G_DYN_STACKALLOC).custom();
1413
1414 getActionDefinitionsBuilder({G_STACKSAVE, G_STACKRESTORE}).lower();
1415
1416 if (ST.hasMOPS()) {
1417 // G_BZERO is not supported. Currently it is only emitted by
1418 // PreLegalizerCombiner for G_MEMSET with zero constant.
1419 getActionDefinitionsBuilder(G_BZERO).unsupported();
1420
1421 getActionDefinitionsBuilder(G_MEMSET)
1422 .legalForCartesianProduct({p0}, {s64}, {s64})
1423 .customForCartesianProduct({p0}, {s8}, {s64})
1424 .immIdx(0); // Inform verifier imm idx 0 is handled.
1425
1426 getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
1427 .legalForCartesianProduct({p0}, {p0}, {s64})
1428 .immIdx(0); // Inform verifier imm idx 0 is handled.
1429
1430 // G_MEMCPY_INLINE does not have a tailcall immediate
1431 getActionDefinitionsBuilder(G_MEMCPY_INLINE)
1432 .legalForCartesianProduct({p0}, {p0}, {s64});
1433
1434 getActionDefinitionsBuilder(G_MEMSET_INLINE)
1435 .legalForCartesianProduct({p0}, {s64}, {s64})
1436 .customForCartesianProduct({p0}, {s8}, {s64});
1437 } else {
1438 getActionDefinitionsBuilder({G_BZERO, G_MEMCPY, G_MEMMOVE, G_MEMSET})
1439 .libcall();
1440 }
1441
1442 // For fadd reductions we have pairwise operations available. We treat the
1443 // usual legal types as legal and handle the lowering to pairwise instructions
1444 // later.
1445 getActionDefinitionsBuilder(G_VECREDUCE_FADD)
1446 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1447 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
1448 .widenScalarIf(
1449 [HasFP16](const LegalityQuery &Query) {
1450 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1451 Query.Types[0].getScalarType().isBFloat16();
1452 },
1453 changeElementTo(0, f32))
1454 .clampMaxNumElements(1, s64, 2)
1455 .clampMaxNumElements(1, s32, 4)
1456 .clampMaxNumElements(1, s16, 8)
1457 .moreElementsToNextPow2(1)
1458 .scalarize(1)
1459 .lower();
1460
1461 // For fmul reductions we need to split up into individual operations. We
1462 // clamp to 128 bit vectors then to 64bit vectors to produce a cascade of
1463 // smaller types, followed by scalarizing what remains.
1464 getActionDefinitionsBuilder(G_VECREDUCE_FMUL)
1465 .widenScalarIf(
1466 [HasFP16](const LegalityQuery &Query) {
1467 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1468 Query.Types[0].getScalarType().isBFloat16();
1469 },
1470 changeElementTo(0, f32))
1471 .clampMaxNumElements(1, s64, 2)
1472 .clampMaxNumElements(1, s32, 4)
1473 .clampMaxNumElements(1, s16, 8)
1474 .clampMaxNumElements(1, s32, 2)
1475 .clampMaxNumElements(1, s16, 4)
1476 .scalarize(1)
1477 .lower();
1478
1479 getActionDefinitionsBuilder({G_VECREDUCE_SEQ_FADD, G_VECREDUCE_SEQ_FMUL})
1480 .scalarize(2)
1481 .lower();
1482
1483 getActionDefinitionsBuilder(G_VECREDUCE_ADD)
1484 .legalFor({{i8, v8i8},
1485 {i8, v16i8},
1486 {i16, v4i16},
1487 {i16, v8i16},
1488 {i32, v2i32},
1489 {i32, v4i32},
1490 {i64, v2i64}})
1492 .clampMaxNumElements(1, s64, 2)
1493 .clampMaxNumElements(1, s32, 4)
1494 .clampMaxNumElements(1, s16, 8)
1495 .clampMaxNumElements(1, s8, 16)
1496 .widenVectorEltsToVectorMinSize(1, 64)
1497 .scalarize(1);
1498
1499 getActionDefinitionsBuilder({G_VECREDUCE_FMIN, G_VECREDUCE_FMAX,
1500 G_VECREDUCE_FMINIMUM, G_VECREDUCE_FMAXIMUM})
1501 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1502 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
1503 .widenScalarIf(
1504 [HasFP16](const LegalityQuery &Query) {
1505 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1506 Query.Types[0].getScalarType().isBFloat16();
1507 },
1508 changeElementTo(0, f32))
1509 .clampMaxNumElements(1, s64, 2)
1510 .clampMaxNumElements(1, s32, 4)
1511 .clampMaxNumElements(1, s16, 8)
1512 .scalarize(1)
1513 .lower();
1514
1515 getActionDefinitionsBuilder(G_VECREDUCE_MUL)
1516 .clampMaxNumElements(1, s32, 2)
1517 .clampMaxNumElements(1, s16, 4)
1518 .clampMaxNumElements(1, s8, 8)
1519 .scalarize(1)
1520 .lower();
1521
1522 getActionDefinitionsBuilder(
1523 {G_VECREDUCE_SMIN, G_VECREDUCE_SMAX, G_VECREDUCE_UMIN, G_VECREDUCE_UMAX})
1524 .legalFor({{i8, v8i8},
1525 {i8, v16i8},
1526 {i16, v4i16},
1527 {i16, v8i16},
1528 {i32, v2i32},
1529 {i32, v4i32}})
1530 .moreElementsIf(
1531 [=](const LegalityQuery &Query) {
1532 return Query.Types[1].isVector() &&
1533 Query.Types[1].getElementType() != s8 &&
1534 Query.Types[1].getNumElements() & 1;
1535 },
1537 .clampMaxNumElements(1, s64, 2)
1538 .clampMaxNumElements(1, s32, 4)
1539 .clampMaxNumElements(1, s16, 8)
1540 .clampMaxNumElements(1, s8, 16)
1541 .scalarize(1)
1542 .lower();
1543
1544 getActionDefinitionsBuilder(
1545 {G_VECREDUCE_OR, G_VECREDUCE_AND, G_VECREDUCE_XOR})
1546 // Try to break down into smaller vectors as long as they're at least 64
1547 // bits. This lets us use vector operations for some parts of the
1548 // reduction.
1549 .fewerElementsIf(
1550 [=](const LegalityQuery &Q) {
1551 LLT SrcTy = Q.Types[1];
1552 if (SrcTy.isScalar())
1553 return false;
1554 if (!isPowerOf2_32(SrcTy.getNumElements()))
1555 return false;
1556 // We can usually perform 64b vector operations.
1557 return SrcTy.getSizeInBits() > 64;
1558 },
1559 [=](const LegalityQuery &Q) {
1560 LLT SrcTy = Q.Types[1];
1561 return std::make_pair(1, SrcTy.divide(2));
1562 })
1563 .scalarize(1)
1564 .lower();
1565
1566 // TODO: Update this to correct handling when adding AArch64/SVE support.
1567 getActionDefinitionsBuilder(G_VECTOR_COMPRESS).lower();
1568
1569 // Access to floating-point environment.
1570 getActionDefinitionsBuilder({G_GET_FPENV, G_SET_FPENV, G_RESET_FPENV,
1571 G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE})
1572 .libcall();
1573
1574 getActionDefinitionsBuilder({G_GET_ROUNDING, G_SET_ROUNDING})
1575 .customFor({s32});
1576
1577 getActionDefinitionsBuilder(G_IS_FPCLASS).lower();
1578
1579 getActionDefinitionsBuilder(G_PREFETCH).custom();
1580
1581 getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
1582
1583 getActionDefinitionsBuilder({G_INTRINSIC, G_INTRINSIC_W_SIDE_EFFECTS})
1584 .alwaysLegal();
1585 getActionDefinitionsBuilder(G_FENCE).alwaysLegal();
1586 getActionDefinitionsBuilder(G_INVOKE_REGION_START).alwaysLegal();
1587
1588 verify(*ST.getInstrInfo());
1589}
1590
1593 LostDebugLocObserver &LocObserver) const {
1594 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
1595 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
1596 GISelChangeObserver &Observer = Helper.Observer;
1597 switch (MI.getOpcode()) {
1598 default:
1599 // No idea what to do.
1600 return false;
1601 case TargetOpcode::G_VAARG:
1602 return legalizeVaArg(MI, MRI, MIRBuilder);
1603 case TargetOpcode::G_LOAD:
1604 case TargetOpcode::G_STORE:
1605 return legalizeLoadStore(MI, MRI, MIRBuilder, Observer);
1606 case TargetOpcode::G_SHL:
1607 case TargetOpcode::G_ASHR:
1608 case TargetOpcode::G_LSHR:
1609 return legalizeShlAshrLshr(MI, MRI, MIRBuilder, Observer);
1610 case TargetOpcode::G_GLOBAL_VALUE:
1611 return legalizeSmallCMGlobalValue(MI, MRI, MIRBuilder, Observer);
1612 case TargetOpcode::G_SBFX:
1613 case TargetOpcode::G_UBFX:
1614 return legalizeBitfieldExtract(MI, MRI, Helper);
1615 case TargetOpcode::G_FSHL:
1616 case TargetOpcode::G_FSHR:
1617 return legalizeFunnelShift(MI, MRI, MIRBuilder, Observer, Helper);
1618 case TargetOpcode::G_ROTR:
1619 return legalizeRotate(MI, MRI, Helper);
1620 case TargetOpcode::G_CTPOP:
1621 return legalizeCTPOP(MI, MRI, Helper);
1622 case TargetOpcode::G_ATOMIC_CMPXCHG:
1623 return legalizeAtomicCmpxchg128(MI, MRI, Helper);
1624 case TargetOpcode::G_CTTZ:
1625 return legalizeCTTZ(MI, Helper);
1626 case TargetOpcode::G_BZERO:
1627 case TargetOpcode::G_MEMCPY:
1628 case TargetOpcode::G_MEMMOVE:
1629 case TargetOpcode::G_MEMSET:
1630 case TargetOpcode::G_MEMSET_INLINE:
1631 return legalizeMemOps(MI, Helper);
1632 case TargetOpcode::G_EXTRACT_VECTOR_ELT:
1633 return legalizeExtractVectorElt(MI, MRI, Helper);
1634 case TargetOpcode::G_DYN_STACKALLOC:
1635 return legalizeDynStackAlloc(MI, Helper);
1636 case TargetOpcode::G_PREFETCH:
1637 return legalizePrefetch(MI, Helper);
1638 case TargetOpcode::G_ABS:
1639 return Helper.lowerAbsToCNeg(MI);
1640 case TargetOpcode::G_ICMP:
1641 return legalizeICMP(MI, MRI, MIRBuilder);
1642 case TargetOpcode::G_BITCAST:
1643 return legalizeBitcast(MI, Helper);
1644 case TargetOpcode::G_CONCAT_VECTORS:
1645 return legalizeConcatVectors(MI, MRI, MIRBuilder);
1646 case TargetOpcode::G_FPTRUNC:
1647 // In order to lower f16 to f64 properly, we need to use f32 as an
1648 // intermediary
1649 return legalizeFptrunc(MI, MIRBuilder, MRI);
1650 case TargetOpcode::G_GET_ROUNDING:
1651 return legalizeGetRounding(MI, MIRBuilder, MRI, Helper);
1652 case TargetOpcode::G_SET_ROUNDING:
1653 return legalizeSetRounding(MI, MIRBuilder, MRI, Helper);
1654 }
1655
1656 llvm_unreachable("expected switch to return");
1657}
1658
1659bool AArch64LegalizerInfo::legalizeBitcast(MachineInstr &MI,
1660 LegalizerHelper &Helper) const {
1661 assert(MI.getOpcode() == TargetOpcode::G_BITCAST && "Unexpected opcode");
1662 auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
1663 // We're trying to handle casts from i1 vectors to scalars but reloading from
1664 // stack.
1665 if (!DstTy.isScalar() || !SrcTy.isVector() ||
1666 SrcTy.getElementType() != LLT::scalar(1))
1667 return false;
1668
1669 Helper.createStackStoreLoad(DstReg, SrcReg);
1670 MI.eraseFromParent();
1671 return true;
1672}
1673
1674bool AArch64LegalizerInfo::legalizeFunnelShift(MachineInstr &MI,
1676 MachineIRBuilder &MIRBuilder,
1677 GISelChangeObserver &Observer,
1678 LegalizerHelper &Helper) const {
1679 assert(MI.getOpcode() == TargetOpcode::G_FSHL ||
1680 MI.getOpcode() == TargetOpcode::G_FSHR);
1681
1682 // Keep as G_FSHR if shift amount is a G_CONSTANT, else use generic
1683 // lowering
1684 Register ShiftNo = MI.getOperand(3).getReg();
1685 LLT ShiftTy = MRI.getType(ShiftNo);
1686 auto VRegAndVal = getIConstantVRegValWithLookThrough(ShiftNo, MRI);
1687
1688 // Adjust shift amount according to Opcode (FSHL/FSHR)
1689 // Convert FSHL to FSHR
1690 LLT OperationTy = MRI.getType(MI.getOperand(0).getReg());
1691 APInt BitWidth(ShiftTy.getSizeInBits(), OperationTy.getSizeInBits(), false);
1692
1693 // Lower non-constant shifts and leave zero shifts to the optimizer.
1694 if (!VRegAndVal || VRegAndVal->Value.urem(BitWidth) == 0)
1695 return (Helper.lowerFunnelShiftAsShifts(MI) ==
1697
1698 APInt Amount = VRegAndVal->Value.urem(BitWidth);
1699
1700 Amount = MI.getOpcode() == TargetOpcode::G_FSHL ? BitWidth - Amount : Amount;
1701
1702 // If the instruction is G_FSHR, has a 64-bit G_CONSTANT for shift amount
1703 // in the range of 0 <-> BitWidth, it is legal
1704 if (ShiftTy.getSizeInBits() == 64 && MI.getOpcode() == TargetOpcode::G_FSHR &&
1705 VRegAndVal->Value.ult(BitWidth))
1706 return true;
1707
1708 // Cast the ShiftNumber to a 64-bit type
1709 auto Cast64 = MIRBuilder.buildConstant(LLT::integer(64), Amount.zext(64));
1710
1711 if (MI.getOpcode() == TargetOpcode::G_FSHR) {
1712 Observer.changingInstr(MI);
1713 MI.getOperand(3).setReg(Cast64.getReg(0));
1714 Observer.changedInstr(MI);
1715 }
1716 // If Opcode is FSHL, remove the FSHL instruction and create a FSHR
1717 // instruction
1718 else if (MI.getOpcode() == TargetOpcode::G_FSHL) {
1719 MIRBuilder.buildInstr(TargetOpcode::G_FSHR, {MI.getOperand(0).getReg()},
1720 {MI.getOperand(1).getReg(), MI.getOperand(2).getReg(),
1721 Cast64.getReg(0)});
1722 MI.eraseFromParent();
1723 }
1724 return true;
1725}
1726
1727bool AArch64LegalizerInfo::legalizeICMP(MachineInstr &MI,
1729 MachineIRBuilder &MIRBuilder) const {
1730 Register DstReg = MI.getOperand(0).getReg();
1731 Register SrcReg1 = MI.getOperand(2).getReg();
1732 Register SrcReg2 = MI.getOperand(3).getReg();
1733 LLT DstTy = MRI.getType(DstReg);
1734 LLT SrcTy = MRI.getType(SrcReg1);
1735
1736 // Check the vector types are legal
1737 if (DstTy.getScalarSizeInBits() != SrcTy.getScalarSizeInBits() ||
1738 DstTy.getNumElements() != SrcTy.getNumElements() ||
1739 (DstTy.getSizeInBits() != 64 && DstTy.getSizeInBits() != 128))
1740 return false;
1741
1742 // Lowers G_ICMP NE => G_ICMP EQ to allow better pattern matching for
1743 // following passes
1744 CmpInst::Predicate Pred = (CmpInst::Predicate)MI.getOperand(1).getPredicate();
1745 if (Pred != CmpInst::ICMP_NE)
1746 return true;
1747 Register CmpReg =
1748 MIRBuilder
1749 .buildICmp(CmpInst::ICMP_EQ, MRI.getType(DstReg), SrcReg1, SrcReg2)
1750 .getReg(0);
1751 MIRBuilder.buildNot(DstReg, CmpReg);
1752
1753 MI.eraseFromParent();
1754 return true;
1755}
1756
1757bool AArch64LegalizerInfo::legalizeRotate(MachineInstr &MI,
1759 LegalizerHelper &Helper) const {
1760 // To allow for imported patterns to match, we ensure that the rotate amount
1761 // is 64b with an extension.
1762 Register AmtReg = MI.getOperand(2).getReg();
1763 LLT AmtTy = MRI.getType(AmtReg);
1764 (void)AmtTy;
1765 assert(AmtTy.isScalar() && "Expected a scalar rotate");
1766 assert(AmtTy.getSizeInBits() < 64 && "Expected this rotate to be legal");
1767 auto NewAmt = Helper.MIRBuilder.buildZExt(LLT::integer(64), AmtReg);
1768 Helper.Observer.changingInstr(MI);
1769 MI.getOperand(2).setReg(NewAmt.getReg(0));
1770 Helper.Observer.changedInstr(MI);
1771 return true;
1772}
1773
1774bool AArch64LegalizerInfo::legalizeSmallCMGlobalValue(
1776 GISelChangeObserver &Observer) const {
1777 assert(MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
1778 // We do this custom legalization to convert G_GLOBAL_VALUE into target ADRP +
1779 // G_ADD_LOW instructions.
1780 // By splitting this here, we can optimize accesses in the small code model by
1781 // folding in the G_ADD_LOW into the load/store offset.
1782 auto &GlobalOp = MI.getOperand(1);
1783 // Don't modify an intrinsic call.
1784 if (GlobalOp.isSymbol())
1785 return true;
1786 const auto* GV = GlobalOp.getGlobal();
1787 if (GV->isThreadLocal())
1788 return true; // Don't want to modify TLS vars.
1789
1790 auto &TM = ST->getTargetLowering()->getTargetMachine();
1791 unsigned OpFlags = ST->ClassifyGlobalReference(GV, TM);
1792
1793 if (OpFlags & AArch64II::MO_GOT)
1794 return true;
1795
1796 auto Offset = GlobalOp.getOffset();
1797 Register DstReg = MI.getOperand(0).getReg();
1798 auto ADRP = MIRBuilder.buildInstr(AArch64::ADRP, {LLT::pointer(0, 64)}, {})
1799 .addGlobalAddress(GV, Offset, OpFlags | AArch64II::MO_PAGE);
1800 // Set the regclass on the dest reg too.
1801 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1802
1803 // MO_TAGGED on the page indicates a tagged address. Set the tag now. We do so
1804 // by creating a MOVK that sets bits 48-63 of the register to (global address
1805 // + 0x100000000 - PC) >> 48. The additional 0x100000000 offset here is to
1806 // prevent an incorrect tag being generated during relocation when the
1807 // global appears before the code section. Without the offset, a global at
1808 // `0x0f00'0000'0000'1000` (i.e. at `0x1000` with tag `0xf`) that's referenced
1809 // by code at `0x2000` would result in `0x0f00'0000'0000'1000 - 0x2000 =
1810 // 0x0eff'ffff'ffff'f000`, meaning the tag would be incorrectly set to `0xe`
1811 // instead of `0xf`.
1812 // This assumes that we're in the small code model so we can assume a binary
1813 // size of <= 4GB, which makes the untagged PC relative offset positive. The
1814 // binary must also be loaded into address range [0, 2^48). Both of these
1815 // properties need to be ensured at runtime when using tagged addresses.
1816 if (OpFlags & AArch64II::MO_TAGGED) {
1817 assert(!Offset &&
1818 "Should not have folded in an offset for a tagged global!");
1819 ADRP = MIRBuilder.buildInstr(AArch64::MOVKXi, {LLT::pointer(0, 64)}, {ADRP})
1820 .addGlobalAddress(GV, 0x100000000,
1822 .addImm(48);
1823 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1824 }
1825
1826 MIRBuilder.buildInstr(AArch64::G_ADD_LOW, {DstReg}, {ADRP})
1827 .addGlobalAddress(GV, Offset,
1829 MI.eraseFromParent();
1830 return true;
1831}
1832
1834 MachineInstr &MI) const {
1835 MachineIRBuilder &MIB = Helper.MIRBuilder;
1836 MachineRegisterInfo &MRI = *MIB.getMRI();
1837
1838 auto LowerUnaryOp = [&MI, &MIB](unsigned Opcode) {
1839 MIB.buildInstr(Opcode, {MI.getOperand(0)}, {MI.getOperand(2)});
1840 MI.eraseFromParent();
1841 return true;
1842 };
1843 auto LowerBinOp = [&MI, &MIB](unsigned Opcode) {
1844 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1845 {MI.getOperand(2), MI.getOperand(3)});
1846 MI.eraseFromParent();
1847 return true;
1848 };
1849 auto LowerTriOp = [&MI, &MIB](unsigned Opcode) {
1850 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1851 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4)});
1852 MI.eraseFromParent();
1853 return true;
1854 };
1855
1856 Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
1857 switch (IntrinsicID) {
1858 case Intrinsic::vacopy: {
1859 MachineFunction &MF = *MI.getMF();
1860 const DataLayout &DL = MF.getDataLayout();
1861 unsigned PtrSize = DL.getPointerSize();
1862 unsigned VaListSize = ST->getTargetLowering()->getVaListSizeInBits(DL) / 8;
1864 LLT::integer(VaListSize * 8));
1865 MIB.buildLoad(Val, MI.getOperand(2),
1868 VaListSize, Align(PtrSize)));
1869 MIB.buildStore(Val, MI.getOperand(1),
1872 VaListSize, Align(PtrSize)));
1873 MI.eraseFromParent();
1874 return true;
1875 }
1876 case Intrinsic::get_dynamic_area_offset: {
1877 MIB.buildConstant(MI.getOperand(0).getReg(), 0);
1878 MI.eraseFromParent();
1879 return true;
1880 }
1881 case Intrinsic::aarch64_mops_memset_tag: {
1882 assert(MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS);
1883 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
1884 // the instruction).
1885 auto &Value = MI.getOperand(3);
1886 Register ExtValueReg = MIB.buildAnyExt(LLT::integer(64), Value).getReg(0);
1887 Value.setReg(ExtValueReg);
1888 return true;
1889 }
1890 case Intrinsic::aarch64_prefetch: {
1891 auto &AddrVal = MI.getOperand(1);
1892
1893 int64_t IsWrite = MI.getOperand(2).getImm();
1894 int64_t Target = MI.getOperand(3).getImm();
1895 int64_t IsStream = MI.getOperand(4).getImm();
1896 int64_t IsData = MI.getOperand(5).getImm();
1897
1898 unsigned PrfOp = (IsWrite << 4) | // Load/Store bit
1899 (!IsData << 3) | // IsDataCache bit
1900 (Target << 1) | // Cache level bits
1901 (unsigned)IsStream; // Stream bit
1902
1903 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
1904 MI.eraseFromParent();
1905 return true;
1906 }
1907 case Intrinsic::aarch64_range_prefetch: {
1908 auto &AddrVal = MI.getOperand(1);
1909
1910 int64_t IsWrite = MI.getOperand(2).getImm();
1911 int64_t IsStream = MI.getOperand(3).getImm();
1912 unsigned PrfOp = (IsStream << 2) | IsWrite;
1913
1914 MIB.buildInstr(AArch64::G_AARCH64_RANGE_PREFETCH)
1915 .addImm(PrfOp)
1916 .add(AddrVal)
1917 .addUse(MI.getOperand(4).getReg()); // Metadata
1918 MI.eraseFromParent();
1919 return true;
1920 }
1921 case Intrinsic::aarch64_prefetch_ir: {
1922 auto &AddrVal = MI.getOperand(1);
1923 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(24).add(AddrVal);
1924 MI.eraseFromParent();
1925 return true;
1926 }
1927 case Intrinsic::aarch64_neon_uaddv:
1928 case Intrinsic::aarch64_neon_saddv:
1929 case Intrinsic::aarch64_neon_umaxv:
1930 case Intrinsic::aarch64_neon_smaxv:
1931 case Intrinsic::aarch64_neon_uminv:
1932 case Intrinsic::aarch64_neon_sminv: {
1933 bool IsSigned = IntrinsicID == Intrinsic::aarch64_neon_saddv ||
1934 IntrinsicID == Intrinsic::aarch64_neon_smaxv ||
1935 IntrinsicID == Intrinsic::aarch64_neon_sminv;
1936
1937 auto OldDst = MI.getOperand(0).getReg();
1938 auto OldDstTy = MRI.getType(OldDst);
1939 LLT NewDstTy = MRI.getType(MI.getOperand(2).getReg()).getElementType();
1940 if (OldDstTy == NewDstTy)
1941 return true;
1942
1943 auto NewDst = MRI.createGenericVirtualRegister(NewDstTy);
1944
1945 Helper.Observer.changingInstr(MI);
1946 MI.getOperand(0).setReg(NewDst);
1947 Helper.Observer.changedInstr(MI);
1948
1949 MIB.setInsertPt(MIB.getMBB(), ++MIB.getInsertPt());
1950 MIB.buildExtOrTrunc(IsSigned ? TargetOpcode::G_SEXT : TargetOpcode::G_ZEXT,
1951 OldDst, NewDst);
1952
1953 return true;
1954 }
1955 case Intrinsic::aarch64_neon_uaddlp:
1956 case Intrinsic::aarch64_neon_saddlp: {
1957 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlp
1958 ? AArch64::G_UADDLP
1959 : AArch64::G_SADDLP;
1960 MIB.buildInstr(Opc, {MI.getOperand(0)}, {MI.getOperand(2)});
1961 MI.eraseFromParent();
1962
1963 return true;
1964 }
1965 case Intrinsic::aarch64_neon_uaddlv:
1966 case Intrinsic::aarch64_neon_saddlv: {
1967 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlv
1968 ? AArch64::G_UADDLV
1969 : AArch64::G_SADDLV;
1970 Register DstReg = MI.getOperand(0).getReg();
1971 Register SrcReg = MI.getOperand(2).getReg();
1972 LLT DstTy = MRI.getType(DstReg);
1973
1974 LLT MidTy, ExtTy;
1975 if (DstTy.isScalar() && DstTy.getScalarSizeInBits() <= 32) {
1976 ExtTy = LLT::integer(32);
1977 MidTy = LLT::fixed_vector(4, ExtTy);
1978 } else {
1979 ExtTy = LLT::integer(64);
1980 MidTy = LLT::fixed_vector(2, ExtTy);
1981 }
1982
1983 Register MidReg =
1984 MIB.buildInstr(Opc, {MidTy}, {SrcReg})->getOperand(0).getReg();
1985 Register ZeroReg =
1986 MIB.buildConstant(LLT::integer(64), 0)->getOperand(0).getReg();
1987 Register ExtReg = MIB.buildInstr(AArch64::G_EXTRACT_VECTOR_ELT, {ExtTy},
1988 {MidReg, ZeroReg})
1989 .getReg(0);
1990
1991 if (DstTy.getScalarSizeInBits() < 32)
1992 MIB.buildTrunc(DstReg, ExtReg);
1993 else
1994 MIB.buildCopy(DstReg, ExtReg);
1995
1996 MI.eraseFromParent();
1997
1998 return true;
1999 }
2000 case Intrinsic::aarch64_neon_fmax:
2001 return LowerBinOp(TargetOpcode::G_FMAXIMUM);
2002 case Intrinsic::aarch64_neon_fmin:
2003 return LowerBinOp(TargetOpcode::G_FMINIMUM);
2004 case Intrinsic::aarch64_neon_fmaxnm:
2005 return LowerBinOp(TargetOpcode::G_FMAXNUM);
2006 case Intrinsic::aarch64_neon_fminnm:
2007 return LowerBinOp(TargetOpcode::G_FMINNUM);
2008 case Intrinsic::aarch64_neon_pmul:
2009 return LowerBinOp(TargetOpcode::G_CLMUL);
2010 case Intrinsic::aarch64_neon_pmull:
2011 case Intrinsic::aarch64_neon_pmull64:
2012 return LowerBinOp(AArch64::G_PMULL);
2013 case Intrinsic::aarch64_neon_smull:
2014 return LowerBinOp(AArch64::G_SMULL);
2015 case Intrinsic::aarch64_neon_umull:
2016 return LowerBinOp(AArch64::G_UMULL);
2017 case Intrinsic::aarch64_neon_sabd:
2018 return LowerBinOp(TargetOpcode::G_ABDS);
2019 case Intrinsic::aarch64_neon_uabd:
2020 return LowerBinOp(TargetOpcode::G_ABDU);
2021 case Intrinsic::aarch64_neon_uhadd:
2022 return LowerBinOp(TargetOpcode::G_UAVGFLOOR);
2023 case Intrinsic::aarch64_neon_urhadd:
2024 return LowerBinOp(TargetOpcode::G_UAVGCEIL);
2025 case Intrinsic::aarch64_neon_shadd:
2026 return LowerBinOp(TargetOpcode::G_SAVGFLOOR);
2027 case Intrinsic::aarch64_neon_srhadd:
2028 return LowerBinOp(TargetOpcode::G_SAVGCEIL);
2029 case Intrinsic::aarch64_neon_sqshrn: {
2030 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2031 return true;
2032 // Create right shift instruction. Store the output register in Shr.
2033 auto Shr = MIB.buildInstr(AArch64::G_VASHR,
2034 {MRI.getType(MI.getOperand(2).getReg())},
2035 {MI.getOperand(2), MI.getOperand(3).getImm()});
2036 // Build the narrow intrinsic, taking in Shr.
2037 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_S, {MI.getOperand(0)}, {Shr});
2038 MI.eraseFromParent();
2039 return true;
2040 }
2041 case Intrinsic::aarch64_neon_sqshrun: {
2042 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2043 return true;
2044 // Create right shift instruction. Store the output register in Shr.
2045 auto Shr = MIB.buildInstr(AArch64::G_VASHR,
2046 {MRI.getType(MI.getOperand(2).getReg())},
2047 {MI.getOperand(2), MI.getOperand(3).getImm()});
2048 // Build the narrow intrinsic, taking in Shr.
2049 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {MI.getOperand(0)}, {Shr});
2050 MI.eraseFromParent();
2051 return true;
2052 }
2053 case Intrinsic::aarch64_neon_sqrshrn: {
2054 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2055 return true;
2056 // Create right shift instruction. Store the output register in Shr.
2057 auto Shr = MIB.buildInstr(AArch64::G_SRSHR_I,
2058 {MRI.getType(MI.getOperand(2).getReg())},
2059 {MI.getOperand(2), MI.getOperand(3).getImm()});
2060 // Build the narrow intrinsic, taking in Shr.
2061 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_S, {MI.getOperand(0)}, {Shr});
2062 MI.eraseFromParent();
2063 return true;
2064 }
2065 case Intrinsic::aarch64_neon_sqrshrun: {
2066 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2067 return true;
2068 // Create right shift instruction. Store the output register in Shr.
2069 auto Shr = MIB.buildInstr(AArch64::G_SRSHR_I,
2070 {MRI.getType(MI.getOperand(2).getReg())},
2071 {MI.getOperand(2), MI.getOperand(3).getImm()});
2072 // Build the narrow intrinsic, taking in Shr.
2073 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {MI.getOperand(0)}, {Shr});
2074 MI.eraseFromParent();
2075 return true;
2076 }
2077 case Intrinsic::aarch64_neon_uqrshrn: {
2078 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2079 return true;
2080 // Create right shift instruction. Store the output register in Shr.
2081 auto Shr = MIB.buildInstr(AArch64::G_URSHR_I,
2082 {MRI.getType(MI.getOperand(2).getReg())},
2083 {MI.getOperand(2), MI.getOperand(3).getImm()});
2084 // Build the narrow intrinsic, taking in Shr.
2085 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2086 MI.eraseFromParent();
2087 return true;
2088 }
2089 case Intrinsic::aarch64_neon_uqshrn: {
2090 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2091 return true;
2092 // Create right shift instruction. Store the output register in Shr.
2093 auto Shr = MIB.buildInstr(AArch64::G_VLSHR,
2094 {MRI.getType(MI.getOperand(2).getReg())},
2095 {MI.getOperand(2), MI.getOperand(3).getImm()});
2096 // Build the narrow intrinsic, taking in Shr.
2097 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2098 MI.eraseFromParent();
2099 return true;
2100 }
2101 case Intrinsic::aarch64_neon_sqshlu: {
2102 // Check if last operand is constant vector dup
2103 auto ShiftAmount =
2104 isConstantOrConstantSplatVector(MI.getOperand(3).getReg(), MRI);
2105 if (ShiftAmount) {
2106 // If so, create a new intrinsic with the correct shift amount
2107 MIB.buildInstr(AArch64::G_SQSHLU_I, {MI.getOperand(0)},
2108 {MI.getOperand(2)})
2109 .addImm(ShiftAmount->getSExtValue());
2110 MI.eraseFromParent();
2111 return true;
2112 }
2113 return false;
2114 }
2115 case Intrinsic::aarch64_neon_vsli: {
2116 MIB.buildInstr(
2117 AArch64::G_SLI, {MI.getOperand(0)},
2118 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2119 MI.eraseFromParent();
2120 break;
2121 }
2122 case Intrinsic::aarch64_neon_vsri: {
2123 MIB.buildInstr(
2124 AArch64::G_SRI, {MI.getOperand(0)},
2125 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2126 MI.eraseFromParent();
2127 break;
2128 }
2129 case Intrinsic::aarch64_neon_abs: {
2130 // Lower the intrinsic to G_ABS.
2131 MIB.buildInstr(TargetOpcode::G_ABS, {MI.getOperand(0)}, {MI.getOperand(2)});
2132 MI.eraseFromParent();
2133 return true;
2134 }
2135 case Intrinsic::aarch64_neon_addhn:
2136 return LowerBinOp(AArch64::G_ADDHN);
2137 case Intrinsic::aarch64_neon_sqadd: {
2138 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2139 return LowerBinOp(TargetOpcode::G_SADDSAT);
2140 break;
2141 }
2142 case Intrinsic::aarch64_neon_sqsub: {
2143 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2144 return LowerBinOp(TargetOpcode::G_SSUBSAT);
2145 break;
2146 }
2147 case Intrinsic::aarch64_neon_uqadd: {
2148 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2149 return LowerBinOp(TargetOpcode::G_UADDSAT);
2150 break;
2151 }
2152 case Intrinsic::aarch64_neon_uqsub: {
2153 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2154 return LowerBinOp(TargetOpcode::G_USUBSAT);
2155 break;
2156 }
2157 case Intrinsic::aarch64_neon_udot:
2158 return LowerTriOp(AArch64::G_UDOT);
2159 case Intrinsic::aarch64_neon_sdot:
2160 return LowerTriOp(AArch64::G_SDOT);
2161 case Intrinsic::aarch64_neon_usdot:
2162 return LowerTriOp(AArch64::G_USDOT);
2163 case Intrinsic::aarch64_neon_sqxtn:
2164 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_S);
2165 case Intrinsic::aarch64_neon_sqxtun:
2166 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_U);
2167 case Intrinsic::aarch64_neon_uqxtn:
2168 return LowerUnaryOp(TargetOpcode::G_TRUNC_USAT_U);
2169 case Intrinsic::aarch64_neon_fcvtzu:
2170 return LowerUnaryOp(TargetOpcode::G_FPTOUI_SAT);
2171 case Intrinsic::aarch64_neon_fcvtzs:
2172 return LowerUnaryOp(TargetOpcode::G_FPTOSI_SAT);
2173 case Intrinsic::aarch64_neon_cls:
2174 return LowerUnaryOp(TargetOpcode::G_CTLS);
2175
2176 case Intrinsic::vector_reverse:
2177 // TODO: Add support for vector_reverse
2178 return false;
2179 }
2180
2181 return true;
2182}
2183
2184bool AArch64LegalizerInfo::legalizeShlAshrLshr(
2186 GISelChangeObserver &Observer) const {
2187 assert(MI.getOpcode() == TargetOpcode::G_ASHR ||
2188 MI.getOpcode() == TargetOpcode::G_LSHR ||
2189 MI.getOpcode() == TargetOpcode::G_SHL);
2190 // If the shift amount is a G_CONSTANT, promote it to a 64 bit type so the
2191 // imported patterns can select it later. Either way, it will be legal.
2192 Register AmtReg = MI.getOperand(2).getReg();
2193 LLT AmtRegEltTy = MRI.getType(AmtReg).getScalarType();
2194 auto VRegAndVal = getIConstantVRegValWithLookThrough(AmtReg, MRI);
2195 if (!VRegAndVal)
2196 return true;
2197 // Check the shift amount is in range for an immediate form.
2198 int64_t Amount = VRegAndVal->Value.getSExtValue();
2199 if (Amount > 31)
2200 return true; // This will have to remain a register variant.
2201 auto ExtCst =
2202 MIRBuilder.buildConstant(AmtRegEltTy.changeElementSize(64), Amount);
2203 Observer.changingInstr(MI);
2204 MI.getOperand(2).setReg(ExtCst.getReg(0));
2205 Observer.changedInstr(MI);
2206 return true;
2207}
2208
2211 Base = Root;
2212 Offset = 0;
2213
2214 Register NewBase;
2215 int64_t NewOffset;
2216 if (mi_match(Root, MRI, m_GPtrAdd(m_Reg(NewBase), m_ICst(NewOffset))) &&
2217 isShiftedInt<7, 3>(NewOffset)) {
2218 Base = NewBase;
2219 Offset = NewOffset;
2220 }
2221}
2222
2223// FIXME: This should be removed and replaced with the generic bitcast legalize
2224// action.
2225bool AArch64LegalizerInfo::legalizeLoadStore(
2227 GISelChangeObserver &Observer) const {
2228 assert(MI.getOpcode() == TargetOpcode::G_STORE ||
2229 MI.getOpcode() == TargetOpcode::G_LOAD);
2230 // Here we just try to handle vector loads/stores where our value type might
2231 // have pointer elements, which the SelectionDAG importer can't handle. To
2232 // allow the existing patterns for s64 to fire for p0, we just try to bitcast
2233 // the value to use s64 types.
2234
2235 // Custom legalization requires the instruction, if not deleted, must be fully
2236 // legalized. In order to allow further legalization of the inst, we create
2237 // a new instruction and erase the existing one.
2238
2239 Register ValReg = MI.getOperand(0).getReg();
2240 const LLT ValTy = MRI.getType(ValReg);
2241
2242 if (ValTy == LLT::scalar(128)) {
2243
2244 AtomicOrdering Ordering = (*MI.memoperands_begin())->getSuccessOrdering();
2245 bool IsLoad = MI.getOpcode() == TargetOpcode::G_LOAD;
2246 bool IsLoadAcquire = IsLoad && Ordering == AtomicOrdering::Acquire;
2247 bool IsStoreRelease = !IsLoad && Ordering == AtomicOrdering::Release;
2248 bool IsRcpC3 =
2249 ST->hasLSE2() && ST->hasRCPC3() && (IsLoadAcquire || IsStoreRelease);
2250
2251 LLT s64 = LLT::integer(64);
2252
2253 unsigned Opcode;
2254 if (IsRcpC3) {
2255 Opcode = IsLoad ? AArch64::LDIAPPX : AArch64::STILPX;
2256 } else {
2257 // For LSE2, loads/stores should have been converted to monotonic and had
2258 // a fence inserted after them.
2259 assert(Ordering == AtomicOrdering::Monotonic ||
2260 Ordering == AtomicOrdering::Unordered);
2261 assert(ST->hasLSE2() && "ldp/stp not single copy atomic without +lse2");
2262
2263 Opcode = IsLoad ? AArch64::LDPXi : AArch64::STPXi;
2264 }
2265
2266 MachineInstrBuilder NewI;
2267 if (IsLoad) {
2268 NewI = MIRBuilder.buildInstr(Opcode, {s64, s64}, {});
2269 MIRBuilder.buildMergeLikeInstr(
2270 ValReg, {NewI->getOperand(0), NewI->getOperand(1)});
2271 } else {
2272 auto Split = MIRBuilder.buildUnmerge(s64, MI.getOperand(0));
2273 NewI = MIRBuilder.buildInstr(
2274 Opcode, {}, {Split->getOperand(0), Split->getOperand(1)});
2275 }
2276
2277 if (IsRcpC3) {
2278 NewI.addUse(MI.getOperand(1).getReg());
2279 } else {
2280 Register Base;
2281 int Offset;
2282 matchLDPSTPAddrMode(MI.getOperand(1).getReg(), Base, Offset, MRI);
2283 NewI.addUse(Base);
2284 NewI.addImm(Offset / 8);
2285 }
2286
2287 NewI.cloneMemRefs(MI);
2288 constrainSelectedInstRegOperands(*NewI, *ST->getInstrInfo(),
2290 *ST->getRegBankInfo());
2291 MI.eraseFromParent();
2292 return true;
2293 }
2294
2295 if (!ValTy.isPointerVector() ||
2296 ValTy.getElementType().getAddressSpace() != 0) {
2297 LLVM_DEBUG(dbgs() << "Tried to do custom legalization on wrong load/store");
2298 return false;
2299 }
2300
2301 unsigned PtrSize = ValTy.getElementType().getSizeInBits();
2302 const LLT NewTy = LLT::vector(ValTy.getElementCount(), LLT::integer(PtrSize));
2303 auto &MMO = **MI.memoperands_begin();
2304 MMO.setType(NewTy);
2305
2306 if (MI.getOpcode() == TargetOpcode::G_STORE) {
2307 auto Bitcast = MIRBuilder.buildBitcast(NewTy, ValReg);
2308 MIRBuilder.buildStore(Bitcast.getReg(0), MI.getOperand(1), MMO);
2309 } else {
2310 auto NewLoad = MIRBuilder.buildLoad(NewTy, MI.getOperand(1), MMO);
2311 MIRBuilder.buildBitcast(ValReg, NewLoad);
2312 }
2313 MI.eraseFromParent();
2314 return true;
2315}
2316
2317bool AArch64LegalizerInfo::legalizeVaArg(MachineInstr &MI,
2319 MachineIRBuilder &MIRBuilder) const {
2320 MachineFunction &MF = MIRBuilder.getMF();
2321 Align Alignment(MI.getOperand(2).getImm());
2322 Register Dst = MI.getOperand(0).getReg();
2323 Register ListPtr = MI.getOperand(1).getReg();
2324
2325 LLT PtrTy = MRI.getType(ListPtr);
2326 LLT IntPtrTy = LLT::integer(PtrTy.getSizeInBits());
2327
2328 const unsigned PtrSize = PtrTy.getSizeInBits() / 8;
2329 const Align PtrAlign = Align(PtrSize);
2330 auto List = MIRBuilder.buildLoad(
2331 PtrTy, ListPtr,
2332 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2333 PtrTy, PtrAlign));
2334
2335 MachineInstrBuilder DstPtr;
2336 if (Alignment > PtrAlign) {
2337 // Realign the list to the actual required alignment.
2338 auto AlignMinus1 =
2339 MIRBuilder.buildConstant(IntPtrTy, Alignment.value() - 1);
2340 auto ListTmp = MIRBuilder.buildPtrAdd(PtrTy, List, AlignMinus1.getReg(0));
2341 DstPtr = MIRBuilder.buildMaskLowPtrBits(PtrTy, ListTmp, Log2(Alignment));
2342 } else
2343 DstPtr = List;
2344
2345 LLT ValTy = MRI.getType(Dst);
2346 uint64_t ValSize = ValTy.getSizeInBits() / 8;
2347 MIRBuilder.buildLoad(
2348 Dst, DstPtr,
2349 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2350 ValTy, std::max(Alignment, PtrAlign)));
2351
2352 auto Size = MIRBuilder.buildConstant(IntPtrTy, alignTo(ValSize, PtrAlign));
2353
2354 auto NewList = MIRBuilder.buildPtrAdd(PtrTy, DstPtr, Size.getReg(0));
2355
2356 MIRBuilder.buildStore(NewList, ListPtr,
2357 *MF.getMachineMemOperand(MachinePointerInfo(),
2359 PtrTy, PtrAlign));
2360
2361 MI.eraseFromParent();
2362 return true;
2363}
2364
2365bool AArch64LegalizerInfo::legalizeBitfieldExtract(
2367 // Only legal if we can select immediate forms.
2368 // TODO: Lower this otherwise.
2369 return getIConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI) &&
2370 getIConstantVRegValWithLookThrough(MI.getOperand(3).getReg(), MRI);
2371}
2372
2373bool AArch64LegalizerInfo::legalizeCTPOP(MachineInstr &MI,
2375 LegalizerHelper &Helper) const {
2376 // When there is no integer popcount instruction (FEAT_CSSC isn't available),
2377 // it can be more efficiently lowered to the following sequence that uses
2378 // AdvSIMD registers/instructions as long as the copies to/from the AdvSIMD
2379 // registers are cheap.
2380 // FMOV D0, X0 // copy 64-bit int to vector, high bits zero'd
2381 // CNT V0.8B, V0.8B // 8xbyte pop-counts
2382 // ADDV B0, V0.8B // sum 8xbyte pop-counts
2383 // UMOV X0, V0.B[0] // copy byte result back to integer reg
2384 //
2385 // For 128 bit vector popcounts, we lower to the following sequence:
2386 // cnt.16b v0, v0 // v8s16, v4s32, v2s64
2387 // uaddlp.8h v0, v0 // v8s16, v4s32, v2s64
2388 // uaddlp.4s v0, v0 // v4s32, v2s64
2389 // uaddlp.2d v0, v0 // v2s64
2390 //
2391 // For 64 bit vector popcounts, we lower to the following sequence:
2392 // cnt.8b v0, v0 // v4s16, v2s32
2393 // uaddlp.4h v0, v0 // v4s16, v2s32
2394 // uaddlp.2s v0, v0 // v2s32
2395
2396 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2397 Register Dst = MI.getOperand(0).getReg();
2398 Register Val = MI.getOperand(1).getReg();
2399 LLT Ty = MRI.getType(Val);
2400
2401 LLT i64 = LLT::integer(64);
2402 LLT i32 = LLT::integer(32);
2403 LLT i16 = LLT::integer(16);
2404 LLT i8 = LLT::integer(8);
2405 unsigned Size = Ty.getSizeInBits();
2406
2407 assert(Ty == MRI.getType(Dst) &&
2408 "Expected src and dst to have the same type!");
2409
2410 if (ST->hasCSSC() && Ty.isScalar() && Size == 128) {
2411
2412 auto Split = MIRBuilder.buildUnmerge(i64, Val);
2413 auto CTPOP1 = MIRBuilder.buildCTPOP(i64, Split->getOperand(0));
2414 auto CTPOP2 = MIRBuilder.buildCTPOP(i64, Split->getOperand(1));
2415 auto Add = MIRBuilder.buildAdd(i64, CTPOP1, CTPOP2);
2416
2417 MIRBuilder.buildZExt(Dst, Add);
2418 MI.eraseFromParent();
2419 return true;
2420 }
2421
2422 if (!ST->hasNEON() ||
2423 MI.getMF()->getFunction().hasFnAttribute(Attribute::NoImplicitFloat)) {
2424 // Use generic lowering when custom lowering is not possible.
2425 return Ty.isScalar() && (Size == 32 || Size == 64) &&
2426 Helper.lowerBitCount(MI) ==
2428 }
2429
2430 // Pre-conditioning: widen Val up to the nearest vector type.
2431 // s32,s64,v4s16,v2s32 -> v8i8
2432 // v8s16,v4s32,v2s64 -> v16i8
2433 LLT VTy = Size == 128 ? LLT::fixed_vector(16, i8) : LLT::fixed_vector(8, i8);
2434 if (Ty.isScalar()) {
2435 assert((Size == 32 || Size == 64 || Size == 128) && "Expected only 32, 64, or 128 bit scalars!");
2436 if (Size == 32) {
2437 Val = MIRBuilder.buildZExt(i64, Val).getReg(0);
2438 }
2439 }
2440 Val = MIRBuilder.buildBitcast(VTy, Val).getReg(0);
2441
2442 // Count bits in each byte-sized lane.
2443 auto CTPOP = MIRBuilder.buildCTPOP(VTy, Val);
2444
2445 // Sum across lanes.
2446 if (ST->hasDotProd() && Ty.isVector() && Ty.getNumElements() >= 2 &&
2447 Ty.getScalarSizeInBits() != 16) {
2448 LLT Dt = Ty == LLT::fixed_vector(2, i64) ? LLT::fixed_vector(4, i32) : Ty;
2449 auto Zeros = MIRBuilder.buildConstant(Dt, 0);
2450 auto Ones = MIRBuilder.buildConstant(VTy, 1);
2451 MachineInstrBuilder Sum;
2452
2453 if (Ty == LLT::fixed_vector(2, i64)) {
2454 auto UDOT =
2455 MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2456 Sum = MIRBuilder.buildInstr(AArch64::G_UADDLP, {Ty}, {UDOT});
2457 } else if (Ty == LLT::fixed_vector(4, i32)) {
2458 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2459 } else if (Ty == LLT::fixed_vector(2, i32)) {
2460 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2461 } else {
2462 llvm_unreachable("unexpected vector shape");
2463 }
2464
2465 Sum->getOperand(0).setReg(Dst);
2466 MI.eraseFromParent();
2467 return true;
2468 }
2469
2470 Register HSum = CTPOP.getReg(0);
2471 unsigned Opc;
2472 SmallVector<LLT> HAddTys;
2473 if (Ty.isScalar()) {
2474 Opc = Intrinsic::aarch64_neon_uaddlv;
2475 HAddTys.push_back(i32);
2476 } else if (Ty == LLT::fixed_vector(8, i16)) {
2477 Opc = Intrinsic::aarch64_neon_uaddlp;
2478 HAddTys.push_back(LLT::fixed_vector(8, i16));
2479 } else if (Ty == LLT::fixed_vector(4, i32)) {
2480 Opc = Intrinsic::aarch64_neon_uaddlp;
2481 HAddTys.push_back(LLT::fixed_vector(8, i16));
2482 HAddTys.push_back(LLT::fixed_vector(4, i32));
2483 } else if (Ty == LLT::fixed_vector(2, i64)) {
2484 Opc = Intrinsic::aarch64_neon_uaddlp;
2485 HAddTys.push_back(LLT::fixed_vector(8, i16));
2486 HAddTys.push_back(LLT::fixed_vector(4, i32));
2487 HAddTys.push_back(LLT::fixed_vector(2, i64));
2488 } else if (Ty == LLT::fixed_vector(4, i16)) {
2489 Opc = Intrinsic::aarch64_neon_uaddlp;
2490 HAddTys.push_back(LLT::fixed_vector(4, i16));
2491 } else if (Ty == LLT::fixed_vector(2, i32)) {
2492 Opc = Intrinsic::aarch64_neon_uaddlp;
2493 HAddTys.push_back(LLT::fixed_vector(4, i16));
2494 HAddTys.push_back(LLT::fixed_vector(2, i32));
2495 } else
2496 llvm_unreachable("unexpected vector shape");
2498 for (LLT HTy : HAddTys) {
2499 UADD = MIRBuilder.buildIntrinsic(Opc, {HTy}).addUse(HSum);
2500 HSum = UADD.getReg(0);
2501 }
2502
2503 // Post-conditioning.
2504 if (Ty.isScalar() && (Size == 64 || Size == 128))
2505 MIRBuilder.buildZExt(Dst, UADD);
2506 else
2507 UADD->getOperand(0).setReg(Dst);
2508 MI.eraseFromParent();
2509 return true;
2510}
2511
2512bool AArch64LegalizerInfo::legalizeAtomicCmpxchg128(
2514 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2515 LLT i64 = LLT::integer(64);
2516 auto Addr = MI.getOperand(1).getReg();
2517 auto DesiredI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(2));
2518 auto NewI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(3));
2519 auto DstLo = MRI.createGenericVirtualRegister(i64);
2520 auto DstHi = MRI.createGenericVirtualRegister(i64);
2521
2522 MachineInstrBuilder CAS;
2523 if (ST->hasLSE()) {
2524 // We have 128-bit CASP instructions taking XSeqPair registers, which are
2525 // s128. We need the merge/unmerge to bracket the expansion and pair up with
2526 // the rest of the MIR so we must reassemble the extracted registers into a
2527 // 128-bit known-regclass one with code like this:
2528 //
2529 // %in1 = REG_SEQUENCE Lo, Hi ; One for each input
2530 // %out = CASP %in1, ...
2531 // %OldLo = G_EXTRACT %out, 0
2532 // %OldHi = G_EXTRACT %out, 64
2533 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2534 unsigned Opcode;
2535 switch (Ordering) {
2537 Opcode = AArch64::CASPAX;
2538 break;
2540 Opcode = AArch64::CASPLX;
2541 break;
2544 Opcode = AArch64::CASPALX;
2545 break;
2546 default:
2547 Opcode = AArch64::CASPX;
2548 break;
2549 }
2550
2551 LLT s128 = LLT::integer(128);
2552 auto CASDst = MRI.createGenericVirtualRegister(s128);
2553 auto CASDesired = MRI.createGenericVirtualRegister(s128);
2554 auto CASNew = MRI.createGenericVirtualRegister(s128);
2555 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASDesired}, {})
2556 .addUse(DesiredI->getOperand(0).getReg())
2557 .addImm(AArch64::sube64)
2558 .addUse(DesiredI->getOperand(1).getReg())
2559 .addImm(AArch64::subo64);
2560 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASNew}, {})
2561 .addUse(NewI->getOperand(0).getReg())
2562 .addImm(AArch64::sube64)
2563 .addUse(NewI->getOperand(1).getReg())
2564 .addImm(AArch64::subo64);
2565
2566 CAS = MIRBuilder.buildInstr(Opcode, {CASDst}, {CASDesired, CASNew, Addr});
2567
2568 MIRBuilder.buildExtract({DstLo}, {CASDst}, 0);
2569 MIRBuilder.buildExtract({DstHi}, {CASDst}, 64);
2570 } else {
2571 // The -O0 CMP_SWAP_128 is friendlier to generate code for because LDXP/STXP
2572 // can take arbitrary registers so it just has the normal GPR64 operands the
2573 // rest of AArch64 is expecting.
2574 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2575 unsigned Opcode;
2576 switch (Ordering) {
2578 Opcode = AArch64::CMP_SWAP_128_ACQUIRE;
2579 break;
2581 Opcode = AArch64::CMP_SWAP_128_RELEASE;
2582 break;
2585 Opcode = AArch64::CMP_SWAP_128;
2586 break;
2587 default:
2588 Opcode = AArch64::CMP_SWAP_128_MONOTONIC;
2589 break;
2590 }
2591
2592 auto Scratch = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
2593 CAS = MIRBuilder.buildInstr(Opcode, {DstLo, DstHi, Scratch},
2594 {Addr, DesiredI->getOperand(0),
2595 DesiredI->getOperand(1), NewI->getOperand(0),
2596 NewI->getOperand(1)});
2597 }
2598
2599 CAS.cloneMemRefs(MI);
2600 constrainSelectedInstRegOperands(*CAS, *ST->getInstrInfo(),
2602 *ST->getRegBankInfo());
2603
2604 MIRBuilder.buildMergeLikeInstr(MI.getOperand(0), {DstLo, DstHi});
2605 MI.eraseFromParent();
2606 return true;
2607}
2608
2609bool AArch64LegalizerInfo::legalizeCTTZ(MachineInstr &MI,
2610 LegalizerHelper &Helper) const {
2611 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2612 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2613 LLT Ty = MRI.getType(MI.getOperand(1).getReg());
2614 auto BitReverse = MIRBuilder.buildBitReverse(Ty, MI.getOperand(1));
2615 MIRBuilder.buildCTLZ(MI.getOperand(0).getReg(), BitReverse);
2616 MI.eraseFromParent();
2617 return true;
2618}
2619
2620bool AArch64LegalizerInfo::legalizeMemOps(MachineInstr &MI,
2621 LegalizerHelper &Helper) const {
2622 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2623
2624 // Tagged version MOPSMemorySetTagged is legalised in legalizeIntrinsic
2625 if (MI.getOpcode() == TargetOpcode::G_MEMSET ||
2626 MI.getOpcode() == TargetOpcode::G_MEMSET_INLINE) {
2627 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
2628 // the instruction).
2629 auto &Value = MI.getOperand(1);
2630 Register ExtValueReg =
2631 MIRBuilder.buildAnyExt(LLT::integer(64), Value).getReg(0);
2632 Value.setReg(ExtValueReg);
2633 return true;
2634 }
2635
2636 return false;
2637}
2638
2639bool AArch64LegalizerInfo::legalizeExtractVectorElt(
2641 const GExtractVectorElement *Element = cast<GExtractVectorElement>(&MI);
2642 auto VRegAndVal =
2644 if (VRegAndVal)
2645 return true;
2646 LLT VecTy = MRI.getType(Element->getVectorReg());
2647 if (VecTy.isScalableVector())
2648 return true;
2649 return Helper.lowerExtractInsertVectorElt(MI) !=
2651}
2652
2653bool AArch64LegalizerInfo::legalizeDynStackAlloc(
2654 MachineInstr &MI, LegalizerHelper &Helper) const {
2655 MachineFunction &MF = *MI.getParent()->getParent();
2656 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2657 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2658
2659 // If stack probing is not enabled for this function, use the default
2660 // lowering.
2661 if (!MF.getFunction().hasFnAttribute("probe-stack") ||
2662 MF.getFunction().getFnAttribute("probe-stack").getValueAsString() !=
2663 "inline-asm") {
2664 Helper.lowerDynStackAlloc(MI);
2665 return true;
2666 }
2667
2668 Register Dst = MI.getOperand(0).getReg();
2669 Register AllocSize = MI.getOperand(1).getReg();
2670 Align Alignment = assumeAligned(MI.getOperand(2).getImm());
2671
2672 assert(MRI.getType(Dst) == LLT::pointer(0, 64) &&
2673 "Unexpected type for dynamic alloca");
2674 assert(MRI.getType(AllocSize) == LLT::scalar(64) &&
2675 "Unexpected type for dynamic alloca");
2676
2677 LLT PtrTy = MRI.getType(Dst);
2678 Register SPReg =
2680 Register SPTmp =
2681 Helper.getDynStackAllocTargetPtr(SPReg, AllocSize, Alignment, PtrTy);
2682 auto NewMI =
2683 MIRBuilder.buildInstr(AArch64::PROBED_STACKALLOC_DYN, {}, {SPTmp});
2684 MRI.setRegClass(NewMI.getReg(0), &AArch64::GPR64commonRegClass);
2685 MIRBuilder.setInsertPt(*NewMI->getParent(), NewMI);
2686 MIRBuilder.buildCopy(Dst, SPTmp);
2687
2688 MI.eraseFromParent();
2689 return true;
2690}
2691
2692bool AArch64LegalizerInfo::legalizePrefetch(MachineInstr &MI,
2693 LegalizerHelper &Helper) const {
2694 MachineIRBuilder &MIB = Helper.MIRBuilder;
2695 auto &AddrVal = MI.getOperand(0);
2696
2697 int64_t IsWrite = MI.getOperand(1).getImm();
2698 int64_t Locality = MI.getOperand(2).getImm();
2699 int64_t IsData = MI.getOperand(3).getImm();
2700
2701 bool IsStream = Locality == 0;
2702 if (Locality != 0) {
2703 assert(Locality <= 3 && "Prefetch locality out-of-range");
2704 // The locality degree is the opposite of the cache speed.
2705 // Put the number the other way around.
2706 // The encoding starts at 0 for level 1
2707 Locality = 3 - Locality;
2708 }
2709
2710 unsigned PrfOp = (IsWrite << 4) | (!IsData << 3) | (Locality << 1) | IsStream;
2711
2712 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
2713 MI.eraseFromParent();
2714 return true;
2715}
2716
2717bool AArch64LegalizerInfo::legalizeConcatVectors(
2719 MachineIRBuilder &MIRBuilder) const {
2720 // Widen sub-byte element vectors to byte-sized elements before concatenating.
2721 // This is analogous to SDAG's integer type promotion for sub-byte types.
2723 Register DstReg = Concat.getReg(0);
2724 LLT DstTy = MRI.getType(DstReg);
2725 assert(DstTy.getScalarSizeInBits() < 8 && "Expected dst ty to be < 8b");
2726
2727 unsigned WideEltSize =
2728 std::max(8u, (unsigned)PowerOf2Ceil(DstTy.getScalarSizeInBits()));
2729 LLT SrcTy = MRI.getType(Concat.getSourceReg(0));
2730 LLT WideSrcTy = SrcTy.changeElementSize(WideEltSize);
2731 LLT WideDstTy = DstTy.changeElementSize(WideEltSize);
2732
2733 SmallVector<Register> WideSrcs;
2734 for (unsigned I = 0; I < Concat.getNumSources(); ++I) {
2735 auto Wide = MIRBuilder.buildAnyExt(WideSrcTy, Concat.getSourceReg(I));
2736 WideSrcs.push_back(Wide.getReg(0));
2737 }
2738
2739 auto WideConcat = MIRBuilder.buildConcatVectors(WideDstTy, WideSrcs);
2740 MIRBuilder.buildTrunc(DstReg, WideConcat);
2741 MI.eraseFromParent();
2742 return true;
2743}
2744
2745bool AArch64LegalizerInfo::legalizeFptrunc(MachineInstr &MI,
2746 MachineIRBuilder &MIRBuilder,
2747 MachineRegisterInfo &MRI) const {
2748 auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
2749
2750 // This function legalizes f64 -> bf16 and f64 -> f16 truncations via f64 ->
2751 // f32 G_FPTRUNC_ODD and f32 -> [b]f16 G_FPTRUNC, which apparently avoids the
2752 // usual double-rounding issue that could be present from using twin
2753 // G_FPTRUNC.
2754
2755 if (DstTy.isBFloat16() && SrcTy.isFloat64()) {
2756 auto Mid = MIRBuilder.buildInstr(AArch64::G_FPTRUNC_ODD, {LLT::float32()},
2757 {Src}, MI.getFlags());
2758 MIRBuilder.buildInstr(AArch64::G_FPTRUNC, {Dst}, {Mid}, MI.getFlags());
2759 MI.eraseFromParent();
2760 return true;
2761 }
2762
2763 assert(SrcTy.isFixedVector() && isPowerOf2_32(SrcTy.getNumElements()) &&
2764 "Expected a power of 2 elements");
2765
2766 // We must mutate types here as FPTrunc may be used on a IEEE floating point
2767 // or a brainfloat.
2768 LLT v2s16 = DstTy.changeElementCount(2);
2769 LLT v4s16 = DstTy.changeElementCount(4);
2770 LLT v2s32 = SrcTy.changeElementCount(2).changeElementSize(32);
2771 LLT v4s32 = SrcTy.changeElementCount(4).changeElementSize(32);
2772 LLT v2s64 = SrcTy.changeElementCount(2);
2773
2774 SmallVector<Register> RegsToUnmergeTo;
2775 SmallVector<Register> TruncOddDstRegs;
2776 SmallVector<Register> RegsToMerge;
2777
2778 unsigned ElemCount = SrcTy.getNumElements();
2779
2780 // Find the biggest size chunks we can work with
2781 int StepSize = ElemCount % 4 ? 2 : 4;
2782
2783 // If we have a power of 2 greater than 2, we need to first unmerge into
2784 // enough pieces
2785 if (ElemCount <= 2)
2786 RegsToUnmergeTo.push_back(Src);
2787 else {
2788 for (unsigned i = 0; i < ElemCount / 2; ++i)
2789 RegsToUnmergeTo.push_back(MRI.createGenericVirtualRegister(v2s64));
2790
2791 MIRBuilder.buildUnmerge(RegsToUnmergeTo, Src);
2792 }
2793
2794 // Create all of the round-to-odd instructions and store them
2795 for (auto SrcReg : RegsToUnmergeTo) {
2796 Register Mid = MIRBuilder
2797 .buildInstr(AArch64::G_FPTRUNC_ODD, {v2s32}, {SrcReg},
2798 MI.getFlags())
2799 .getReg(0);
2800 TruncOddDstRegs.push_back(Mid);
2801 }
2802
2803 // Truncate 4s32 to 4s16 if we can to reduce instruction count, otherwise
2804 // truncate 2s32 to 2s16.
2805 unsigned Index = 0;
2806 for (unsigned LoopIter = 0; LoopIter < ElemCount / StepSize; ++LoopIter) {
2807 if (StepSize == 4) {
2808 Register ConcatDst =
2809 MIRBuilder
2811 {v4s32}, {TruncOddDstRegs[Index++], TruncOddDstRegs[Index++]})
2812 .getReg(0);
2813
2814 RegsToMerge.push_back(
2815 MIRBuilder.buildFPTrunc(v4s16, ConcatDst, MI.getFlags()).getReg(0));
2816 } else {
2817 RegsToMerge.push_back(
2818 MIRBuilder
2819 .buildFPTrunc(v2s16, TruncOddDstRegs[Index++], MI.getFlags())
2820 .getReg(0));
2821 }
2822 }
2823
2824 // If there is only one register, replace the destination
2825 if (RegsToMerge.size() == 1) {
2826 MRI.replaceRegWith(Dst, RegsToMerge.pop_back_val());
2827 MI.eraseFromParent();
2828 return true;
2829 }
2830
2831 // Merge the rest of the instructions & replace the register
2832 Register Fin = MIRBuilder.buildMergeLikeInstr(DstTy, RegsToMerge).getReg(0);
2833 MRI.replaceRegWith(Dst, Fin);
2834 MI.eraseFromParent();
2835 return true;
2836}
2837
2838bool AArch64LegalizerInfo::legalizeGetRounding(MachineInstr &MI,
2839 MachineIRBuilder &MIRBuilder,
2841 LegalizerHelper &Helper) const {
2842 const LLT I32 = LLT::integer(32);
2843 const LLT I64 = LLT::integer(64);
2844
2845 Register Dst = MI.getOperand(0).getReg();
2847 MachineInstrBuilder GetFPCR =
2848 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, ArrayRef{FPCR64});
2849
2850 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2851 // 3->0, so we add one to the FPCR bits for the rounding mode.
2852 // Instead of shifting and then adding as `((FPCR >> 22) + 1) & 0b11` which
2853 // generates 3 instructions, we increment the rounding mode with
2854 // `(FPCR + (1 << 22))` and extract the bits. The shift and addition is done
2855 // in one instruction as `add .., .., #1024, lsl #12`, so overall we generate
2856 // one less instruction.
2857 auto FPCR32 = MIRBuilder.buildTrunc(I32, GetFPCR);
2858 auto One = MIRBuilder.buildConstant(I32, 1U << 22);
2859 auto Added = MIRBuilder.buildAdd(I32, FPCR32, One);
2860 auto LSB = MIRBuilder.buildConstant(I32, 22);
2861 auto Width = MIRBuilder.buildConstant(I32, 2);
2862 MIRBuilder.buildInstr(TargetOpcode::G_UBFX, {Dst}, {Added, LSB, Width});
2863
2864 MI.eraseFromParent();
2865 return true;
2866}
2867
2868bool AArch64LegalizerInfo::legalizeSetRounding(MachineInstr &MI,
2869 MachineIRBuilder &MIRBuilder,
2871 LegalizerHelper &Helper) const {
2872 const LLT I32 = LLT::integer(32);
2873 const LLT I64 = LLT::integer(64);
2874
2875 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2876 // 3->0, so calculate the new value of FPCR[23:22] as `((arg - 1) & 3) << 22`.
2877 Register RM = MI.getOperand(0).getReg();
2878 auto One = MIRBuilder.buildConstant(I32, 1);
2879 auto Subtracted = MIRBuilder.buildSub(I32, RM, One);
2880 auto Mask = MIRBuilder.buildConstant(I32, 0b11);
2881 auto Masked = MIRBuilder.buildAnd(I32, Subtracted, Mask);
2882 auto ShiftAmount = MIRBuilder.buildConstant(I32, 22);
2883 auto Shifted = MIRBuilder.buildShl(I32, Masked, ShiftAmount);
2884
2885 // Get current value of FPCR.
2886 MachineInstrBuilder GetFPCR =
2887 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, {I64});
2888
2889 // (FPCR & ~Mask) | Shifted
2890 auto FPCRMask = MIRBuilder.buildConstant(I64, ~((int64_t)0b11 << 22));
2891 auto FPCRMasked = MIRBuilder.buildAnd(I64, GetFPCR, FPCRMask);
2892 auto ShiftedS64 = MIRBuilder.buildZExt(I64, Shifted);
2893 auto FPCRUpdated = MIRBuilder.buildOr(I64, FPCRMasked, ShiftedS64);
2894
2895 // Write new FPCR.
2896 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_set_fpcr, ArrayRef<Register>())
2897 .addUse(FPCRUpdated.getReg(0));
2898
2899 MI.eraseFromParent();
2900
2901 return true;
2902}
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
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
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:1518
@ Offset
Definition DWP.cpp:577
LLVM_ABI void constrainSelectedInstRegOperands(MachineInstr &I, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI)
Mutate the newly-selected instruction I to constrain its (possibly generic) virtual register operands...
Definition Utils.cpp:159
constexpr bool isPowerOf2_64(uint64_t Value)
Return true if the argument is a power of two > 0 (64 bit edition.)
Definition MathExtras.h:285
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
uint64_t PowerOf2Ceil(uint64_t A)
Returns the power of two which is greater than or equal to the given value.
Definition MathExtras.h:380
std::function< bool(const LegalityQuery &)> LegalityPredicate
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h: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:436
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,...