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