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