LLVM 24.0.0git
AArch64LegalizerInfo.cpp
Go to the documentation of this file.
1//===- AArch64LegalizerInfo.cpp ----------------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9/// This file implements the targeting of the Machinelegalizer class for
10/// AArch64.
11/// \todo This should be generated by TableGen.
12//===----------------------------------------------------------------------===//
13
15#include "AArch64Subtarget.h"
16#include "llvm/ADT/STLExtras.h"
28#include "llvm/IR/Intrinsics.h"
29#include "llvm/IR/IntrinsicsAArch64.h"
30#include "llvm/IR/Type.h"
32#include <initializer_list>
33
34#define DEBUG_TYPE "aarch64-legalinfo"
35
36using namespace llvm;
37using namespace LegalizeActions;
38using namespace LegalizeMutations;
39using namespace LegalityPredicates;
40using namespace MIPatternMatch;
41
43 : ST(&ST) {
44 using namespace TargetOpcode;
45 const LLT p0 = LLT::pointer(0, 64);
46 const LLT s8 = LLT::scalar(8);
47 const LLT s16 = LLT::scalar(16);
48 const LLT s32 = LLT::scalar(32);
49 const LLT s64 = LLT::scalar(64);
50 const LLT s128 = LLT::scalar(128);
51 const LLT v16s8 = LLT::fixed_vector(16, 8);
52 const LLT v8s8 = LLT::fixed_vector(8, 8);
53 const LLT v4s8 = LLT::fixed_vector(4, 8);
54 const LLT v2s8 = LLT::fixed_vector(2, 8);
55 const LLT v8s16 = LLT::fixed_vector(8, 16);
56 const LLT v4s16 = LLT::fixed_vector(4, 16);
57 const LLT v2s16 = LLT::fixed_vector(2, 16);
58 const LLT v2s32 = LLT::fixed_vector(2, 32);
59 const LLT v4s32 = LLT::fixed_vector(4, 32);
60 const LLT v2s64 = LLT::fixed_vector(2, 64);
61 const LLT v2p0 = LLT::fixed_vector(2, p0);
62
63 const LLT nxv16s8 = LLT::scalable_vector(16, s8);
64 const LLT nxv8s16 = LLT::scalable_vector(8, s16);
65 const LLT nxv4s32 = LLT::scalable_vector(4, s32);
66 const LLT nxv2s64 = LLT::scalable_vector(2, s64);
67
68 const LLT bf16 = LLT::bfloat16();
69 const LLT v4bf16 = LLT::fixed_vector(4, bf16);
70 const LLT v8bf16 = LLT::fixed_vector(8, bf16);
71
72 const LLT f16 = LLT::float16();
73 const LLT v4f16 = LLT::fixed_vector(4, f16);
74 const LLT v8f16 = LLT::fixed_vector(8, f16);
75
76 const LLT f32 = LLT::float32();
77 const LLT v2f32 = LLT::fixed_vector(2, f32);
78 const LLT v4f32 = LLT::fixed_vector(4, f32);
79
80 const LLT f64 = LLT::float64();
81 const LLT v2f64 = LLT::fixed_vector(2, f64);
82
83 const LLT f128 = LLT::float128();
84
85 const LLT i8 = LLT::integer(8);
86 const LLT v8i8 = LLT::fixed_vector(8, i8);
87 const LLT v16i8 = LLT::fixed_vector(16, i8);
88
89 const LLT i16 = LLT::integer(16);
90 const LLT v8i16 = LLT::fixed_vector(8, i16);
91 const LLT v4i16 = LLT::fixed_vector(4, i16);
92
93 const LLT i32 = LLT::integer(32);
94 const LLT v2i32 = LLT::fixed_vector(2, i32);
95 const LLT v4i32 = LLT::fixed_vector(4, i32);
96
97 const LLT i64 = LLT::integer(64);
98 const LLT v2i64 = LLT::fixed_vector(2, i64);
99
100 const LLT i128 = LLT::integer(128);
101
102 const LLT nxv16i8 = LLT::scalable_vector(16, i8);
103 const LLT nxv8i16 = LLT::scalable_vector(8, i16);
104 const LLT nxv4i32 = LLT::scalable_vector(4, i32);
105 const LLT nxv2i64 = LLT::scalable_vector(2, i64);
106
107 std::initializer_list<LLT> PackedVectorAllTypeList = {/* Begin 128bit types */
108 v16s8, v8s16, v4s32,
109 v2s64, v2p0,
110 /* End 128bit types */
111 /* Begin 64bit types */
112 v8s8, v4s16, v2s32};
113 std::initializer_list<LLT> ScalarAndPtrTypesList = {s8, s16, s32, s64, p0};
114 SmallVector<LLT, 8> PackedVectorAllTypesVec(PackedVectorAllTypeList);
115 SmallVector<LLT, 8> ScalarAndPtrTypesVec(ScalarAndPtrTypesList);
116
117 const TargetMachine &TM = ST.getTargetLowering()->getTargetMachine();
118
119 // FIXME: support subtargets which have neon/fp-armv8 disabled.
120 if (!ST.hasNEON() || !ST.hasFPARMv8())
121 return;
122
123 // Some instructions only support s16 if the subtarget has full 16-bit FP
124 // support.
125 const bool HasFP16 = ST.hasFullFP16();
126 const bool HasCSSC = ST.hasCSSC();
127 const bool HasRCPC3 = ST.hasRCPC3();
128 const bool HasSVE = ST.hasSVE();
129
131 {G_IMPLICIT_DEF, G_FREEZE, G_CONSTANT_FOLD_BARRIER})
132 .legalFor({p0, s8, s16, s32, s64, s128})
133 .legalFor({v2s8, v4s8, v8s8, v16s8, v2s16, v4s16, v8s16, v2s32, v4s32,
134 v2s64, v2p0})
135 .widenScalarToNextPow2(0)
136 .clampScalar(0, s8, s64)
139 .clampNumElements(0, v8s8, v16s8)
140 .clampNumElements(0, v4s16, v8s16)
141 .clampNumElements(0, v2s32, v4s32)
142 .clampMaxNumElements(0, s64, 2)
143 .clampMaxNumElements(0, p0, 2)
145
147 .legalFor({p0, s16, s32, s64})
148 .legalFor(PackedVectorAllTypeList)
152 .clampScalar(0, s16, s64)
153 .clampNumElements(0, v8s8, v16s8)
154 .clampNumElements(0, v4s16, v8s16)
155 .clampNumElements(0, v2s32, v4s32)
156 .clampMaxNumElements(0, s64, 2)
157 .clampMaxNumElements(0, p0, 2)
159
161 .legalIf(all(typeInSet(0, {s32, s64, p0}), typeInSet(1, {s8, s16, s32}),
162 smallerThan(1, 0)))
163 .widenScalarToNextPow2(0)
164 .clampScalar(0, s32, s64)
166 .minScalar(1, s8)
167 .maxScalarIf(typeInSet(0, {s32}), 1, s16)
168 .maxScalarIf(typeInSet(0, {s64, p0}), 1, s32);
169
171 .legalIf(all(typeInSet(0, {s16, s32, s64, p0}),
172 typeInSet(1, {s32, s64, s128, p0}), smallerThan(0, 1)))
173 .widenScalarToNextPow2(1)
174 .clampScalar(1, s32, s128)
176 .minScalar(0, s16)
177 .maxScalarIf(typeInSet(1, {s32}), 0, s16)
178 .maxScalarIf(typeInSet(1, {s64, p0}), 0, s32)
179 .maxScalarIf(typeInSet(1, {s128}), 0, s64);
180
181 getActionDefinitionsBuilder({G_ADD, G_SUB, G_AND, G_OR, G_XOR})
182 .legalFor({i32, i64, v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
183 .legalFor(HasSVE, {nxv16i8, nxv8i16, nxv4i32, nxv2i64})
184 .widenScalarToNextPow2(0)
185 .clampScalar(0, s32, s64)
186 .clampMaxNumElements(0, s8, 16)
187 .clampMaxNumElements(0, s16, 8)
188 .clampNumElements(0, v2s32, v4s32)
189 .clampNumElements(0, v2s64, v2s64)
191 [=](const LegalityQuery &Query) {
192 return Query.Types[0].getNumElements() <= 2;
193 },
194 0, s32)
195 .minScalarOrEltIf(
196 [=](const LegalityQuery &Query) {
197 return Query.Types[0].getNumElements() <= 4;
198 },
199 0, s16)
200 .minScalarOrEltIf(
201 [=](const LegalityQuery &Query) {
202 return Query.Types[0].getNumElements() <= 16;
203 },
204 0, s8)
205 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
207
209 .legalFor({i32, i64, v8i8, v16i8, v4i16, v8i16, v2i32, v4i32, v2i64})
210 .widenScalarToNextPow2(0)
211 .clampScalar(0, s32, s64)
212 .clampMaxNumElements(0, s8, 16)
213 .clampMaxNumElements(0, s16, 8)
214 .clampNumElements(0, v2s32, v4s32)
215 .clampNumElements(0, v2s64, v2s64)
217 [=](const LegalityQuery &Query) {
218 return Query.Types[0].getNumElements() <= 2;
219 },
220 0, s32)
221 .minScalarOrEltIf(
222 [=](const LegalityQuery &Query) {
223 return Query.Types[0].getNumElements() <= 4;
224 },
225 0, s16)
226 .minScalarOrEltIf(
227 [=](const LegalityQuery &Query) {
228 return Query.Types[0].getNumElements() <= 16;
229 },
230 0, s8)
231 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
233
234 getActionDefinitionsBuilder({G_SHL, G_ASHR, G_LSHR})
235 .customIf([=](const LegalityQuery &Query) {
236 const auto &SrcTy = Query.Types[0];
237 const auto &AmtTy = Query.Types[1];
238 return !SrcTy.isVector() && SrcTy.getSizeInBits() == 32 &&
239 AmtTy.getSizeInBits() == 32;
240 })
241 .legalFor({
242 {i32, i32},
243 {i32, i64},
244 {i64, i64},
245 {v8i8, v8i8},
246 {v16i8, v16i8},
247 {v4i16, v4i16},
248 {v8i16, v8i16},
249 {v2i32, v2i32},
250 {v4i32, v4i32},
251 {v2i64, v2i64},
252 })
253 .widenScalarToNextPow2(1)
255 .clampScalar(1, s32, s64)
256 .clampScalar(0, s32, s64)
257 .clampNumElements(0, v8s8, v16s8)
258 .clampNumElements(0, v4s16, v8s16)
259 .clampNumElements(0, v2s32, v4s32)
260 .clampNumElements(0, v2s64, v2s64)
262 .minScalarSameAs(1, 0)
266
268 .legalFor({{p0, i64}, {v2p0, v2i64}})
269 .clampScalarOrElt(1, s64, s64)
270 .clampNumElements(0, v2p0, v2p0);
271
272 getActionDefinitionsBuilder(G_PTRMASK).legalFor({{p0, s64}});
273
274 getActionDefinitionsBuilder({G_SDIV, G_UDIV})
275 .legalFor({i32, i64})
276 .libcallFor({i128})
277 .clampScalar(0, s32, s64)
279 .scalarize(0);
280
281 getActionDefinitionsBuilder({G_SREM, G_UREM, G_SDIVREM, G_UDIVREM})
282 .lowerFor({i8, i16, i32, i64, v2i32, v4i32, v2i64})
283 .libcallFor({i128})
285 .minScalarOrElt(0, s32)
286 .clampNumElements(0, v2s32, v4s32)
287 .clampNumElements(0, v2s64, v2s64)
288 .scalarize(0);
289
290 getActionDefinitionsBuilder({G_SMULO, G_UMULO})
291 .widenScalarToNextPow2(0, /*Min = */ 32)
292 .clampScalar(0, s32, s64)
293 .lower();
294
295 getActionDefinitionsBuilder({G_SMULH, G_UMULH})
296 .legalFor({i64, v16i8, v8i16, v4i32})
297 .lower();
298
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 changeElementSizeTo(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)
938 [HasFP16](const LegalityQuery &Query) {
939 return (!HasFP16 && Query.Types[1].getScalarType().isFloat16()) ||
940 Query.Types[1].getScalarType().isBFloat16();
941 },
942 changeElementTo(1, f32))
943 .widenScalarIf(
944 [=](const LegalityQuery &Query) {
945 return Query.Types[0].getScalarSizeInBits() <= 64 &&
946 Query.Types[0].getScalarSizeInBits() >
947 Query.Types[1].getScalarSizeInBits();
948 },
950 .widenScalarIf(
951 [=](const LegalityQuery &Query) {
952 return Query.Types[1].getScalarSizeInBits() <= 64 &&
953 Query.Types[0].getScalarSizeInBits() <
954 Query.Types[1].getScalarSizeInBits();
955 },
957 .clampNumElements(0, v4s16, v8s16)
958 .clampNumElements(0, v2s32, v4s32)
959 .clampMaxNumElements(0, s64, 2)
960 .libcallFor(
961 {{i32, f128}, {i64, f128}, {i128, f128}, {i128, f32}, {i128, f64}});
962
963 getActionDefinitionsBuilder({G_FPTOSI_SAT, G_FPTOUI_SAT})
964 .legalFor({{i32, f32},
965 {i64, f32},
966 {i32, f64},
967 {i64, f64},
968 {v2i32, v2f32},
969 {v4i32, v4f32},
970 {v2i64, v2f64}})
971 .legalFor(
972 HasFP16,
973 {{i16, f16}, {i32, f16}, {i64, f16}, {v4i16, v4f16}, {v8i16, v8f16}})
974 // Handle types larger than i64 by scalarizing/lowering.
975 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
977 // The range of a fp16 value fits into an i17, so we can lower the width
978 // to i64.
980 [=](const LegalityQuery &Query) {
981 return Query.Types[1] == f16 && Query.Types[0].getSizeInBits() > 64;
982 },
983 changeTo(0, i64))
984 .lowerIf(::any(scalarWiderThan(0, 64), scalarWiderThan(1, 64)), 0)
986 .widenScalarToNextPow2(0, /*MinSize=*/32)
987 .minScalar(0, s32)
989 [HasFP16](const LegalityQuery &Query) {
990 return (!HasFP16 && Query.Types[1].getScalarType().isFloat16()) ||
991 Query.Types[1].getScalarType().isBFloat16();
992 },
993 changeElementTo(1, f32))
994 .widenScalarIf(
995 [=](const LegalityQuery &Query) {
996 unsigned ITySize = Query.Types[0].getScalarSizeInBits();
997 return (ITySize == 16 || ITySize == 32 || ITySize == 64) &&
998 ITySize > Query.Types[1].getScalarSizeInBits();
999 },
1001 .widenScalarIf(
1002 [=](const LegalityQuery &Query) {
1003 unsigned FTySize = Query.Types[1].getScalarSizeInBits();
1004 return (FTySize == 16 || FTySize == 32 || FTySize == 64) &&
1005 Query.Types[0].getScalarSizeInBits() < FTySize;
1006 },
1009 .clampNumElements(0, v4s16, v8s16)
1010 .clampNumElements(0, v2s32, v4s32)
1011 .clampMaxNumElements(0, s64, 2);
1012
1013 getActionDefinitionsBuilder({G_SITOFP, G_UITOFP})
1014 .legalFor({{f32, i32},
1015 {f64, i32},
1016 {f32, i64},
1017 {f64, i64},
1018 {v2f32, v2i32},
1019 {v4f32, v4i32},
1020 {v2f64, v2i64}})
1021 .legalFor(HasFP16,
1022 {{f16, i32}, {f16, i64}, {v4f16, v4i16}, {v8f16, v8i16}})
1023 .unsupportedIf([&](const LegalityQuery &Query) {
1024 return Query.Types[0].getScalarType().isBFloat16();
1025 })
1026 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1)
1030 .minScalar(1, f32)
1031 .lowerIf([](const LegalityQuery &Query) {
1032 return Query.Types[1].isVector() &&
1033 Query.Types[1].getScalarSizeInBits() == 64 &&
1034 Query.Types[0].getScalarSizeInBits() == 16;
1035 })
1036 .widenScalarOrEltToNextPow2OrMinSize(0, /*MinSize=*/HasFP16 ? 16 : 32)
1037 .scalarizeIf(
1038 // v2i64->v2f32 needs to scalarize to avoid double-rounding issues.
1039 [](const LegalityQuery &Query) {
1040 return Query.Types[0].getScalarSizeInBits() == 32 &&
1041 Query.Types[1].getScalarSizeInBits() == 64;
1042 },
1043 0)
1044 .widenScalarIf(
1045 [](const LegalityQuery &Query) {
1046 return Query.Types[1].getScalarSizeInBits() <= 64 &&
1047 Query.Types[0].getScalarSizeInBits() <
1048 Query.Types[1].getScalarSizeInBits();
1049 },
1051 .widenScalarIf(
1052 [](const LegalityQuery &Query) {
1053 return Query.Types[0].getScalarSizeInBits() <= 64 &&
1054 Query.Types[0].getScalarSizeInBits() >
1055 Query.Types[1].getScalarSizeInBits();
1056 },
1058 .clampNumElements(0, v4s16, v8s16)
1059 .clampNumElements(0, v2s32, v4s32)
1060 .clampMaxNumElements(0, s64, 2)
1061 .libcallFor({{f16, i128},
1062 {f32, i128},
1063 {f64, i128},
1064 {f128, i128},
1065 {f128, i32},
1066 {f128, i64}});
1067
1068 // Control-flow
1069 getActionDefinitionsBuilder(G_BR).alwaysLegal();
1070 getActionDefinitionsBuilder(G_BRCOND)
1071 .legalFor({s32})
1072 .clampScalar(0, s32, s32);
1073 getActionDefinitionsBuilder(G_BRINDIRECT).legalFor({p0});
1074
1075 getActionDefinitionsBuilder(G_SELECT)
1076 .legalFor({{s32, s32}, {s64, s32}, {p0, s32}})
1077 .widenScalarToNextPow2(0)
1078 .clampScalar(0, s32, s64)
1079 .clampScalar(1, s32, s32)
1082 .lowerIf(isVector(0));
1083
1084 // Pointer-handling
1085 getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
1086
1087 if (TM.getCodeModel() == CodeModel::Small)
1088 getActionDefinitionsBuilder(G_GLOBAL_VALUE).custom();
1089 else
1090 getActionDefinitionsBuilder(G_GLOBAL_VALUE).legalFor({p0});
1091
1092 getActionDefinitionsBuilder(G_PTRAUTH_GLOBAL_VALUE)
1093 .legalIf(all(typeIs(0, p0), typeIs(1, p0)));
1094
1095 getActionDefinitionsBuilder(G_PTRTOINT)
1096 .legalFor({{i64, p0}, {v2i64, v2p0}})
1097 .widenScalarToNextPow2(0, 64)
1098 .clampScalar(0, s64, s64)
1099 .clampMaxNumElements(0, s64, 2);
1100
1101 getActionDefinitionsBuilder(G_INTTOPTR)
1102 .unsupportedIf([&](const LegalityQuery &Query) {
1103 return Query.Types[0].getSizeInBits() != Query.Types[1].getSizeInBits();
1104 })
1105 .legalFor({{p0, i64}, {v2p0, v2i64}})
1106 .clampMaxNumElements(1, s64, 2);
1107
1108 // Casts for 32 and 64-bit width type are just copies.
1109 // Same for 128-bit width type, except they are on the FPR bank.
1110 getActionDefinitionsBuilder(G_BITCAST)
1112 // Keeping 32-bit instructions legal to prevent regression in some tests
1113 .legalForCartesianProduct({s32, v2s16, v4s8})
1114 .legalForCartesianProduct({s64, v8s8, v4s16, v2s32})
1115 .legalForCartesianProduct({s128, v16s8, v8s16, v4s32, v2s64, v2p0})
1116 .customIf([=](const LegalityQuery &Query) {
1117 // Handle casts from i1 vectors to scalars.
1118 LLT DstTy = Query.Types[0];
1119 LLT SrcTy = Query.Types[1];
1120 return DstTy.isScalar() && SrcTy.isVector() &&
1121 SrcTy.getScalarSizeInBits() == 1;
1122 })
1123 .lowerIf([=](const LegalityQuery &Query) {
1124 return Query.Types[0].isVector() != Query.Types[1].isVector();
1125 })
1127 .clampNumElements(0, v8s8, v16s8)
1128 .clampNumElements(0, v4s16, v8s16)
1129 .clampNumElements(0, v2s32, v4s32)
1130 .clampMaxNumElements(0, s64, 2)
1131 .lower();
1132
1133 getActionDefinitionsBuilder(G_VASTART).legalFor({p0});
1134
1135 // va_list must be a pointer, but most sized types are pretty easy to handle
1136 // as the destination.
1137 getActionDefinitionsBuilder(G_VAARG)
1138 .customForCartesianProduct({s8, s16, s32, s64, p0}, {p0})
1139 .clampScalar(0, s8, s64)
1140 .widenScalarToNextPow2(0, /*Min*/ 8);
1141
1142 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG_WITH_SUCCESS)
1143 .lowerIf(
1144 all(typeInSet(0, {s8, s16, s32, s64, s128}), typeIs(2, p0)));
1145
1146 bool UseOutlineAtomics = ST.outlineAtomics() && !ST.hasLSE();
1147
1148 getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG)
1149 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1150 .customFor(!UseOutlineAtomics, {{s128, p0}})
1151 .libcallFor(UseOutlineAtomics,
1152 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}, {s128, p0}})
1153 .clampScalar(0, s32, s64);
1154
1155 getActionDefinitionsBuilder({G_ATOMICRMW_XCHG, G_ATOMICRMW_ADD,
1156 G_ATOMICRMW_SUB, G_ATOMICRMW_AND, G_ATOMICRMW_OR,
1157 G_ATOMICRMW_XOR})
1158 .legalFor(!UseOutlineAtomics, {{s32, p0}, {s64, p0}})
1159 .libcallFor(UseOutlineAtomics,
1160 {{s8, p0}, {s16, p0}, {s32, p0}, {s64, p0}})
1161 .clampScalar(0, s32, s64);
1162
1163 // Do not outline these atomics operations, as per comment in
1164 // AArch64ISelLowering.cpp's shouldExpandAtomicRMWInIR().
1165 getActionDefinitionsBuilder(
1166 {G_ATOMICRMW_MIN, G_ATOMICRMW_MAX, G_ATOMICRMW_UMIN, G_ATOMICRMW_UMAX})
1167 .legalIf(all(typeInSet(0, {s32, s64}), typeIs(1, p0)))
1168 .clampScalar(0, s32, s64);
1169
1170 getActionDefinitionsBuilder(G_BLOCK_ADDR).legalFor({p0});
1171
1172 // Merge/Unmerge
1173 for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) {
1174 unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1;
1175 unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0;
1176 getActionDefinitionsBuilder(Op)
1177 .widenScalarToNextPow2(LitTyIdx, 8)
1178 .widenScalarToNextPow2(BigTyIdx, 32)
1179 .clampScalar(LitTyIdx, s8, s64)
1180 .clampScalar(BigTyIdx, s32, s128)
1181 .legalIf([=](const LegalityQuery &Q) {
1182 switch (Q.Types[BigTyIdx].getSizeInBits()) {
1183 case 32:
1184 case 64:
1185 case 128:
1186 break;
1187 default:
1188 return false;
1189 }
1190 switch (Q.Types[LitTyIdx].getSizeInBits()) {
1191 case 8:
1192 case 16:
1193 case 32:
1194 case 64:
1195 return true;
1196 default:
1197 return false;
1198 }
1199 });
1200 }
1201
1202 // TODO : nxv4s16, nxv2s16, nxv2s32
1203 getActionDefinitionsBuilder(G_EXTRACT_VECTOR_ELT)
1204 .legalFor(HasSVE, {{s16, nxv16s8, s64},
1205 {s16, nxv8s16, s64},
1206 {s32, nxv4s32, s64},
1207 {s64, nxv2s64, s64}})
1208 .unsupportedIf([=](const LegalityQuery &Query) {
1209 const LLT &EltTy = Query.Types[1].getElementType();
1210 if (Query.Types[1].isScalableVector())
1211 return false;
1212 return Query.Types[0] != EltTy;
1213 })
1214 .minScalar(2, s64)
1215 .customIf([=](const LegalityQuery &Query) {
1216 const LLT &VecTy = Query.Types[1];
1217 return VecTy == v8s8 || VecTy == v16s8 || VecTy == v2s16 ||
1218 VecTy == v4s16 || VecTy == v8s16 || VecTy == v2s32 ||
1219 VecTy == v4s32 || VecTy == v2s64 || VecTy == v2p0;
1220 })
1221 .minScalarOrEltIf(
1222 [=](const LegalityQuery &Query) {
1223 // We want to promote to <M x s1> to <M x s64> if that wouldn't
1224 // cause the total vec size to be > 128b.
1225 return Query.Types[1].isFixedVector() &&
1226 Query.Types[1].getNumElements() <= 2;
1227 },
1228 0, s64)
1229 .minScalarOrEltIf(
1230 [=](const LegalityQuery &Query) {
1231 return Query.Types[1].isFixedVector() &&
1232 Query.Types[1].getNumElements() <= 4;
1233 },
1234 0, s32)
1235 .minScalarOrEltIf(
1236 [=](const LegalityQuery &Query) {
1237 return Query.Types[1].isFixedVector() &&
1238 Query.Types[1].getNumElements() <= 8;
1239 },
1240 0, s16)
1241 .minScalarOrEltIf(
1242 [=](const LegalityQuery &Query) {
1243 return Query.Types[1].isFixedVector() &&
1244 Query.Types[1].getNumElements() <= 16;
1245 },
1246 0, s8)
1247 .minScalarOrElt(0, s8) // Worst case, we need at least s8.
1248 .moreElementsToNextPow2(1)
1249 .clampMaxNumElements(1, s64, 2)
1250 .clampMaxNumElements(1, s32, 4)
1251 .clampMaxNumElements(1, s16, 8)
1252 .clampMaxNumElements(1, s8, 16)
1253 .clampMaxNumElements(1, p0, 2)
1254 .scalarizeIf(scalarOrEltWiderThan(1, 64), 1);
1255
1256 getActionDefinitionsBuilder(G_INSERT_VECTOR_ELT)
1257 .legalIf(
1258 typeInSet(0, {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64, v2p0}))
1259 .legalFor(HasSVE, {{nxv16s8, s32, s64},
1260 {nxv8s16, s32, s64},
1261 {nxv4s32, s32, s64},
1262 {nxv2s64, s64, s64}})
1264 .widenVectorEltsToVectorMinSize(0, 64)
1265 .clampNumElements(0, v8s8, v16s8)
1266 .clampNumElements(0, v4s16, v8s16)
1267 .clampNumElements(0, v2s32, v4s32)
1268 .clampMaxNumElements(0, s64, 2)
1269 .clampMaxNumElements(0, p0, 2)
1270 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
1271
1272 getActionDefinitionsBuilder(G_BUILD_VECTOR)
1273 .legalFor({{v8s8, s8},
1274 {v16s8, s8},
1275 {v4s16, s16},
1276 {v8s16, s16},
1277 {v2s32, s32},
1278 {v4s32, s32},
1279 {v2s64, s64},
1280 {v2p0, p0}})
1281 .clampNumElements(0, v4s32, v4s32)
1282 .clampNumElements(0, v2s64, v2s64)
1283 .minScalarOrElt(0, s8)
1284 .widenVectorEltsToVectorMinSize(0, 64)
1285 .widenScalarOrEltToNextPow2(0)
1286 .minScalarSameAs(1, 0);
1287
1288 getActionDefinitionsBuilder(G_BUILD_VECTOR_TRUNC).lower();
1289
1290 getActionDefinitionsBuilder(G_SHUFFLE_VECTOR)
1291 .legalIf([=](const LegalityQuery &Query) {
1292 const LLT &DstTy = Query.Types[0];
1293 const LLT &SrcTy = Query.Types[1];
1294 // For now just support the TBL2 variant which needs the source vectors
1295 // to be the same size as the dest.
1296 if (DstTy != SrcTy)
1297 return false;
1298 return llvm::is_contained(
1299 {v8s8, v16s8, v4s16, v8s16, v2s32, v4s32, v2s64}, DstTy);
1300 })
1301 .moreElementsIf(
1302 [](const LegalityQuery &Query) {
1303 return Query.Types[0].getNumElements() >
1304 Query.Types[1].getNumElements();
1305 },
1306 changeTo(1, 0))
1308 .moreElementsIf(
1309 [](const LegalityQuery &Query) {
1310 return Query.Types[0].getNumElements() <
1311 Query.Types[1].getNumElements();
1312 },
1313 changeTo(0, 1))
1314 .widenScalarOrEltToNextPow2OrMinSize(0, 8)
1315 .clampNumElements(0, v8s8, v16s8)
1316 .clampNumElements(0, v4s16, v8s16)
1317 .clampNumElements(0, v4s32, v4s32)
1318 .clampNumElements(0, v2s64, v2s64)
1319 .scalarizeIf(scalarOrEltWiderThan(0, 64), 0)
1320 .bitcastIf(isPointerVector(0), [=](const LegalityQuery &Query) {
1321 // Bitcast pointers vector to i64.
1322 const LLT DstTy = Query.Types[0];
1323 return std::pair(
1324 0, LLT::vector(DstTy.getElementCount(), LLT::integer(64)));
1325 });
1326
1327 getActionDefinitionsBuilder(G_CONCAT_VECTORS)
1328 .legalFor({{v16s8, v8s8}, {v8s16, v4s16}, {v4s32, v2s32}})
1329 .customIf([=](const LegalityQuery &Query) {
1330 return Query.Types[0].isFixedVector() &&
1331 Query.Types[0].getScalarSizeInBits() < 8;
1332 })
1333 .bitcastIf(
1334 [=](const LegalityQuery &Query) {
1335 return Query.Types[0].isFixedVector() &&
1336 Query.Types[1].isFixedVector() &&
1337 Query.Types[0].getScalarSizeInBits() >= 8 &&
1338 isPowerOf2_64(Query.Types[0].getScalarSizeInBits()) &&
1339 Query.Types[0].getSizeInBits() <= 128 &&
1340 Query.Types[1].getSizeInBits() <= 64;
1341 },
1342 [=](const LegalityQuery &Query) {
1343 const LLT DstTy = Query.Types[0];
1344 const LLT SrcTy = Query.Types[1];
1345 return std::pair(
1346 0, DstTy.changeElementSize(SrcTy.getSizeInBits())
1349 SrcTy.getNumElements())));
1350 });
1351
1352 getActionDefinitionsBuilder(G_EXTRACT_SUBVECTOR)
1353 .legalFor({{v8s8, v16s8}, {v4s16, v8s16}, {v2s32, v4s32}})
1355 .clampMaxNumElements(0, s8, 16)
1356 .clampMaxNumElements(0, s16, 8)
1357 .clampMaxNumElements(0, s32, 4)
1358 .clampNumElements(1, v8s8, v16s8)
1359 .clampNumElements(1, v4s16, v8s16)
1360 .clampNumElements(1, v2s32, v4s32)
1361 .lower()
1362 .immIdx(0); // Inform verifier imm idx 0 is handled.
1363
1364 // TODO: {nxv16s8, s8}, {nxv8s16, s16}
1365 getActionDefinitionsBuilder(G_SPLAT_VECTOR)
1366 .legalFor(HasSVE, {{nxv4s32, s32}, {nxv2s64, s64}});
1367
1368 getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
1369
1370 getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, s64}});
1371
1372 getActionDefinitionsBuilder({G_TRAP, G_DEBUGTRAP, G_UBSANTRAP}).alwaysLegal();
1373
1374 getActionDefinitionsBuilder(G_DYN_STACKALLOC).custom();
1375
1376 getActionDefinitionsBuilder({G_STACKSAVE, G_STACKRESTORE}).lower();
1377
1378 if (ST.hasMOPS()) {
1379 // G_BZERO is not supported. Currently it is only emitted by
1380 // PreLegalizerCombiner for G_MEMSET with zero constant.
1381 getActionDefinitionsBuilder(G_BZERO).unsupported();
1382
1383 getActionDefinitionsBuilder(G_MEMSET)
1384 .legalForCartesianProduct({p0}, {s64}, {s64})
1385 .customForCartesianProduct({p0}, {s8}, {s64})
1386 .immIdx(0); // Inform verifier imm idx 0 is handled.
1387
1388 getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
1389 .legalForCartesianProduct({p0}, {p0}, {s64})
1390 .immIdx(0); // Inform verifier imm idx 0 is handled.
1391
1392 // G_MEMCPY_INLINE does not have a tailcall immediate
1393 getActionDefinitionsBuilder(G_MEMCPY_INLINE)
1394 .legalForCartesianProduct({p0}, {p0}, {s64});
1395
1396 getActionDefinitionsBuilder(G_MEMSET_INLINE)
1397 .legalForCartesianProduct({p0}, {s64}, {s64})
1398 .customForCartesianProduct({p0}, {s8}, {s64});
1399 } else {
1400 getActionDefinitionsBuilder({G_BZERO, G_MEMCPY, G_MEMMOVE, G_MEMSET})
1401 .libcall();
1402 }
1403
1404 // For fadd reductions we have pairwise operations available. We treat the
1405 // usual legal types as legal and handle the lowering to pairwise instructions
1406 // later.
1407 getActionDefinitionsBuilder(G_VECREDUCE_FADD)
1408 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1409 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
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 .moreElementsToNextPow2(1)
1420 .scalarize(1)
1421 .lower();
1422
1423 // For fmul reductions we need to split up into individual operations. We
1424 // clamp to 128 bit vectors then to 64bit vectors to produce a cascade of
1425 // smaller types, followed by scalarizing what remains.
1426 getActionDefinitionsBuilder(G_VECREDUCE_FMUL)
1427 .widenScalarIf(
1428 [HasFP16](const LegalityQuery &Query) {
1429 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1430 Query.Types[0].getScalarType().isBFloat16();
1431 },
1432 changeElementTo(0, f32))
1433 .clampMaxNumElements(1, s64, 2)
1434 .clampMaxNumElements(1, s32, 4)
1435 .clampMaxNumElements(1, s16, 8)
1436 .clampMaxNumElements(1, s32, 2)
1437 .clampMaxNumElements(1, s16, 4)
1438 .scalarize(1)
1439 .lower();
1440
1441 getActionDefinitionsBuilder({G_VECREDUCE_SEQ_FADD, G_VECREDUCE_SEQ_FMUL})
1442 .scalarize(2)
1443 .lower();
1444
1445 getActionDefinitionsBuilder(G_VECREDUCE_ADD)
1446 .legalFor({{i8, v8i8},
1447 {i8, v16i8},
1448 {i16, v4i16},
1449 {i16, v8i16},
1450 {i32, v2i32},
1451 {i32, v4i32},
1452 {i64, v2i64}})
1454 .clampMaxNumElements(1, s64, 2)
1455 .clampMaxNumElements(1, s32, 4)
1456 .clampMaxNumElements(1, s16, 8)
1457 .clampMaxNumElements(1, s8, 16)
1458 .widenVectorEltsToVectorMinSize(1, 64)
1459 .scalarize(1);
1460
1461 getActionDefinitionsBuilder({G_VECREDUCE_FMIN, G_VECREDUCE_FMAX,
1462 G_VECREDUCE_FMINIMUM, G_VECREDUCE_FMAXIMUM})
1463 .legalFor({{f32, v2f32}, {f32, v4f32}, {f64, v2f64}})
1464 .legalFor(HasFP16, {{f16, v4f16}, {f16, v8f16}})
1465 .widenScalarIf(
1466 [HasFP16](const LegalityQuery &Query) {
1467 return (!HasFP16 && Query.Types[0].getScalarType().isFloat16()) ||
1468 Query.Types[0].getScalarType().isBFloat16();
1469 },
1470 changeElementTo(0, f32))
1471 .clampMaxNumElements(1, s64, 2)
1472 .clampMaxNumElements(1, s32, 4)
1473 .clampMaxNumElements(1, s16, 8)
1474 .scalarize(1)
1475 .lower();
1476
1477 getActionDefinitionsBuilder(G_VECREDUCE_MUL)
1478 .clampMaxNumElements(1, s32, 2)
1479 .clampMaxNumElements(1, s16, 4)
1480 .clampMaxNumElements(1, s8, 8)
1481 .scalarize(1)
1482 .lower();
1483
1484 getActionDefinitionsBuilder(
1485 {G_VECREDUCE_SMIN, G_VECREDUCE_SMAX, G_VECREDUCE_UMIN, G_VECREDUCE_UMAX})
1486 .legalFor({{i8, v8i8},
1487 {i8, v16i8},
1488 {i16, v4i16},
1489 {i16, v8i16},
1490 {i32, v2i32},
1491 {i32, v4i32}})
1492 .moreElementsIf(
1493 [=](const LegalityQuery &Query) {
1494 return Query.Types[1].isVector() &&
1495 Query.Types[1].getElementType() != s8 &&
1496 Query.Types[1].getNumElements() & 1;
1497 },
1499 .clampMaxNumElements(1, s64, 2)
1500 .clampMaxNumElements(1, s32, 4)
1501 .clampMaxNumElements(1, s16, 8)
1502 .clampMaxNumElements(1, s8, 16)
1503 .scalarize(1)
1504 .lower();
1505
1506 getActionDefinitionsBuilder(
1507 {G_VECREDUCE_OR, G_VECREDUCE_AND, G_VECREDUCE_XOR})
1508 // Try to break down into smaller vectors as long as they're at least 64
1509 // bits. This lets us use vector operations for some parts of the
1510 // reduction.
1511 .fewerElementsIf(
1512 [=](const LegalityQuery &Q) {
1513 LLT SrcTy = Q.Types[1];
1514 if (SrcTy.isScalar())
1515 return false;
1516 if (!isPowerOf2_32(SrcTy.getNumElements()))
1517 return false;
1518 // We can usually perform 64b vector operations.
1519 return SrcTy.getSizeInBits() > 64;
1520 },
1521 [=](const LegalityQuery &Q) {
1522 LLT SrcTy = Q.Types[1];
1523 return std::make_pair(1, SrcTy.divide(2));
1524 })
1525 .scalarize(1)
1526 .lower();
1527
1528 // TODO: Update this to correct handling when adding AArch64/SVE support.
1529 getActionDefinitionsBuilder(G_VECTOR_COMPRESS).lower();
1530
1531 // Access to floating-point environment.
1532 getActionDefinitionsBuilder({G_GET_FPENV, G_SET_FPENV, G_RESET_FPENV,
1533 G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE})
1534 .libcall();
1535
1536 getActionDefinitionsBuilder({G_GET_ROUNDING, G_SET_ROUNDING})
1537 .customFor({s32});
1538
1539 getActionDefinitionsBuilder(G_IS_FPCLASS).lower();
1540
1541 getActionDefinitionsBuilder(G_PREFETCH).custom();
1542
1543 getActionDefinitionsBuilder({G_SCMP, G_UCMP}).lower();
1544
1545 getActionDefinitionsBuilder({G_INTRINSIC, G_INTRINSIC_W_SIDE_EFFECTS})
1546 .alwaysLegal();
1547 getActionDefinitionsBuilder(G_FENCE).alwaysLegal();
1548 getActionDefinitionsBuilder(G_INVOKE_REGION_START).alwaysLegal();
1549
1550 verify(*ST.getInstrInfo());
1551}
1552
1555 LostDebugLocObserver &LocObserver) const {
1556 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
1557 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
1558 GISelChangeObserver &Observer = Helper.Observer;
1559 switch (MI.getOpcode()) {
1560 default:
1561 // No idea what to do.
1562 return false;
1563 case TargetOpcode::G_VAARG:
1564 return legalizeVaArg(MI, MRI, MIRBuilder);
1565 case TargetOpcode::G_LOAD:
1566 case TargetOpcode::G_STORE:
1567 return legalizeLoadStore(MI, MRI, MIRBuilder, Observer);
1568 case TargetOpcode::G_SHL:
1569 case TargetOpcode::G_ASHR:
1570 case TargetOpcode::G_LSHR:
1571 return legalizeShlAshrLshr(MI, MRI, MIRBuilder, Observer);
1572 case TargetOpcode::G_GLOBAL_VALUE:
1573 return legalizeSmallCMGlobalValue(MI, MRI, MIRBuilder, Observer);
1574 case TargetOpcode::G_SBFX:
1575 case TargetOpcode::G_UBFX:
1576 return legalizeBitfieldExtract(MI, MRI, Helper);
1577 case TargetOpcode::G_FSHL:
1578 case TargetOpcode::G_FSHR:
1579 return legalizeFunnelShift(MI, MRI, MIRBuilder, Observer, Helper);
1580 case TargetOpcode::G_ROTR:
1581 return legalizeRotate(MI, MRI, Helper);
1582 case TargetOpcode::G_CTPOP:
1583 return legalizeCTPOP(MI, MRI, Helper);
1584 case TargetOpcode::G_ATOMIC_CMPXCHG:
1585 return legalizeAtomicCmpxchg128(MI, MRI, Helper);
1586 case TargetOpcode::G_CTTZ:
1587 return legalizeCTTZ(MI, Helper);
1588 case TargetOpcode::G_BZERO:
1589 case TargetOpcode::G_MEMCPY:
1590 case TargetOpcode::G_MEMMOVE:
1591 case TargetOpcode::G_MEMSET:
1592 case TargetOpcode::G_MEMSET_INLINE:
1593 return legalizeMemOps(MI, Helper);
1594 case TargetOpcode::G_EXTRACT_VECTOR_ELT:
1595 return legalizeExtractVectorElt(MI, MRI, Helper);
1596 case TargetOpcode::G_DYN_STACKALLOC:
1597 return legalizeDynStackAlloc(MI, Helper);
1598 case TargetOpcode::G_PREFETCH:
1599 return legalizePrefetch(MI, Helper);
1600 case TargetOpcode::G_ABS:
1601 return Helper.lowerAbsToCNeg(MI);
1602 case TargetOpcode::G_ICMP:
1603 return legalizeICMP(MI, MRI, MIRBuilder);
1604 case TargetOpcode::G_BITCAST:
1605 return legalizeBitcast(MI, Helper);
1606 case TargetOpcode::G_CONCAT_VECTORS:
1607 return legalizeConcatVectors(MI, MRI, MIRBuilder);
1608 case TargetOpcode::G_FPTRUNC:
1609 // In order to lower f16 to f64 properly, we need to use f32 as an
1610 // intermediary
1611 return legalizeFptrunc(MI, MIRBuilder, MRI);
1612 case TargetOpcode::G_GET_ROUNDING:
1613 return legalizeGetRounding(MI, MIRBuilder, MRI, Helper);
1614 case TargetOpcode::G_SET_ROUNDING:
1615 return legalizeSetRounding(MI, MIRBuilder, MRI, Helper);
1616 }
1617
1618 llvm_unreachable("expected switch to return");
1619}
1620
1621bool AArch64LegalizerInfo::legalizeBitcast(MachineInstr &MI,
1622 LegalizerHelper &Helper) const {
1623 assert(MI.getOpcode() == TargetOpcode::G_BITCAST && "Unexpected opcode");
1624 auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
1625 // We're trying to handle casts from i1 vectors to scalars but reloading from
1626 // stack.
1627 if (!DstTy.isScalar() || !SrcTy.isVector() ||
1628 SrcTy.getElementType() != LLT::scalar(1))
1629 return false;
1630
1631 Helper.createStackStoreLoad(DstReg, SrcReg);
1632 MI.eraseFromParent();
1633 return true;
1634}
1635
1636bool AArch64LegalizerInfo::legalizeFunnelShift(MachineInstr &MI,
1638 MachineIRBuilder &MIRBuilder,
1639 GISelChangeObserver &Observer,
1640 LegalizerHelper &Helper) const {
1641 assert(MI.getOpcode() == TargetOpcode::G_FSHL ||
1642 MI.getOpcode() == TargetOpcode::G_FSHR);
1643
1644 // Keep as G_FSHR if shift amount is a G_CONSTANT, else use generic
1645 // lowering
1646 Register ShiftNo = MI.getOperand(3).getReg();
1647 LLT ShiftTy = MRI.getType(ShiftNo);
1648 auto VRegAndVal = getIConstantVRegValWithLookThrough(ShiftNo, MRI);
1649
1650 // Adjust shift amount according to Opcode (FSHL/FSHR)
1651 // Convert FSHL to FSHR
1652 LLT OperationTy = MRI.getType(MI.getOperand(0).getReg());
1653 APInt BitWidth(ShiftTy.getSizeInBits(), OperationTy.getSizeInBits(), false);
1654
1655 // Lower non-constant shifts and leave zero shifts to the optimizer.
1656 if (!VRegAndVal || VRegAndVal->Value.urem(BitWidth) == 0)
1657 return (Helper.lowerFunnelShiftAsShifts(MI) ==
1659
1660 APInt Amount = VRegAndVal->Value.urem(BitWidth);
1661
1662 Amount = MI.getOpcode() == TargetOpcode::G_FSHL ? BitWidth - Amount : Amount;
1663
1664 // If the instruction is G_FSHR, has a 64-bit G_CONSTANT for shift amount
1665 // in the range of 0 <-> BitWidth, it is legal
1666 if (ShiftTy.getSizeInBits() == 64 && MI.getOpcode() == TargetOpcode::G_FSHR &&
1667 VRegAndVal->Value.ult(BitWidth))
1668 return true;
1669
1670 // Cast the ShiftNumber to a 64-bit type
1671 auto Cast64 = MIRBuilder.buildConstant(LLT::integer(64), Amount.zext(64));
1672
1673 if (MI.getOpcode() == TargetOpcode::G_FSHR) {
1674 Observer.changingInstr(MI);
1675 MI.getOperand(3).setReg(Cast64.getReg(0));
1676 Observer.changedInstr(MI);
1677 }
1678 // If Opcode is FSHL, remove the FSHL instruction and create a FSHR
1679 // instruction
1680 else if (MI.getOpcode() == TargetOpcode::G_FSHL) {
1681 MIRBuilder.buildInstr(TargetOpcode::G_FSHR, {MI.getOperand(0).getReg()},
1682 {MI.getOperand(1).getReg(), MI.getOperand(2).getReg(),
1683 Cast64.getReg(0)});
1684 MI.eraseFromParent();
1685 }
1686 return true;
1687}
1688
1689bool AArch64LegalizerInfo::legalizeICMP(MachineInstr &MI,
1691 MachineIRBuilder &MIRBuilder) const {
1692 Register DstReg = MI.getOperand(0).getReg();
1693 Register SrcReg1 = MI.getOperand(2).getReg();
1694 Register SrcReg2 = MI.getOperand(3).getReg();
1695 LLT DstTy = MRI.getType(DstReg);
1696 LLT SrcTy = MRI.getType(SrcReg1);
1697
1698 // Check the vector types are legal
1699 if (DstTy.getScalarSizeInBits() != SrcTy.getScalarSizeInBits() ||
1700 DstTy.getNumElements() != SrcTy.getNumElements() ||
1701 (DstTy.getSizeInBits() != 64 && DstTy.getSizeInBits() != 128))
1702 return false;
1703
1704 // Lowers G_ICMP NE => G_ICMP EQ to allow better pattern matching for
1705 // following passes
1706 CmpInst::Predicate Pred = (CmpInst::Predicate)MI.getOperand(1).getPredicate();
1707 if (Pred != CmpInst::ICMP_NE)
1708 return true;
1709 Register CmpReg =
1710 MIRBuilder
1711 .buildICmp(CmpInst::ICMP_EQ, MRI.getType(DstReg), SrcReg1, SrcReg2)
1712 .getReg(0);
1713 MIRBuilder.buildNot(DstReg, CmpReg);
1714
1715 MI.eraseFromParent();
1716 return true;
1717}
1718
1719bool AArch64LegalizerInfo::legalizeRotate(MachineInstr &MI,
1721 LegalizerHelper &Helper) const {
1722 // To allow for imported patterns to match, we ensure that the rotate amount
1723 // is 64b with an extension.
1724 Register AmtReg = MI.getOperand(2).getReg();
1725 LLT AmtTy = MRI.getType(AmtReg);
1726 (void)AmtTy;
1727 assert(AmtTy.isScalar() && "Expected a scalar rotate");
1728 assert(AmtTy.getSizeInBits() < 64 && "Expected this rotate to be legal");
1729 auto NewAmt = Helper.MIRBuilder.buildZExt(LLT::integer(64), AmtReg);
1730 Helper.Observer.changingInstr(MI);
1731 MI.getOperand(2).setReg(NewAmt.getReg(0));
1732 Helper.Observer.changedInstr(MI);
1733 return true;
1734}
1735
1736bool AArch64LegalizerInfo::legalizeSmallCMGlobalValue(
1738 GISelChangeObserver &Observer) const {
1739 assert(MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
1740 // We do this custom legalization to convert G_GLOBAL_VALUE into target ADRP +
1741 // G_ADD_LOW instructions.
1742 // By splitting this here, we can optimize accesses in the small code model by
1743 // folding in the G_ADD_LOW into the load/store offset.
1744 auto &GlobalOp = MI.getOperand(1);
1745 // Don't modify an intrinsic call.
1746 if (GlobalOp.isSymbol())
1747 return true;
1748 const auto* GV = GlobalOp.getGlobal();
1749 if (GV->isThreadLocal())
1750 return true; // Don't want to modify TLS vars.
1751
1752 auto &TM = ST->getTargetLowering()->getTargetMachine();
1753 unsigned OpFlags = ST->ClassifyGlobalReference(GV, TM);
1754
1755 if (OpFlags & AArch64II::MO_GOT)
1756 return true;
1757
1758 auto Offset = GlobalOp.getOffset();
1759 Register DstReg = MI.getOperand(0).getReg();
1760 auto ADRP = MIRBuilder.buildInstr(AArch64::ADRP, {LLT::pointer(0, 64)}, {})
1761 .addGlobalAddress(GV, Offset, OpFlags | AArch64II::MO_PAGE);
1762 // Set the regclass on the dest reg too.
1763 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1764
1765 // MO_TAGGED on the page indicates a tagged address. Set the tag now. We do so
1766 // by creating a MOVK that sets bits 48-63 of the register to (global address
1767 // + 0x100000000 - PC) >> 48. The additional 0x100000000 offset here is to
1768 // prevent an incorrect tag being generated during relocation when the
1769 // global appears before the code section. Without the offset, a global at
1770 // `0x0f00'0000'0000'1000` (i.e. at `0x1000` with tag `0xf`) that's referenced
1771 // by code at `0x2000` would result in `0x0f00'0000'0000'1000 - 0x2000 =
1772 // 0x0eff'ffff'ffff'f000`, meaning the tag would be incorrectly set to `0xe`
1773 // instead of `0xf`.
1774 // This assumes that we're in the small code model so we can assume a binary
1775 // size of <= 4GB, which makes the untagged PC relative offset positive. The
1776 // binary must also be loaded into address range [0, 2^48). Both of these
1777 // properties need to be ensured at runtime when using tagged addresses.
1778 if (OpFlags & AArch64II::MO_TAGGED) {
1779 assert(!Offset &&
1780 "Should not have folded in an offset for a tagged global!");
1781 ADRP = MIRBuilder.buildInstr(AArch64::MOVKXi, {LLT::pointer(0, 64)}, {ADRP})
1782 .addGlobalAddress(GV, 0x100000000,
1784 .addImm(48);
1785 MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
1786 }
1787
1788 MIRBuilder.buildInstr(AArch64::G_ADD_LOW, {DstReg}, {ADRP})
1789 .addGlobalAddress(GV, Offset,
1791 MI.eraseFromParent();
1792 return true;
1793}
1794
1796 MachineInstr &MI) const {
1797 MachineIRBuilder &MIB = Helper.MIRBuilder;
1798 MachineRegisterInfo &MRI = *MIB.getMRI();
1799
1800 auto LowerUnaryOp = [&MI, &MIB](unsigned Opcode) {
1801 MIB.buildInstr(Opcode, {MI.getOperand(0)}, {MI.getOperand(2)});
1802 MI.eraseFromParent();
1803 return true;
1804 };
1805 auto LowerBinOp = [&MI, &MIB](unsigned Opcode) {
1806 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1807 {MI.getOperand(2), MI.getOperand(3)});
1808 MI.eraseFromParent();
1809 return true;
1810 };
1811 auto LowerTriOp = [&MI, &MIB](unsigned Opcode) {
1812 MIB.buildInstr(Opcode, {MI.getOperand(0)},
1813 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4)});
1814 MI.eraseFromParent();
1815 return true;
1816 };
1817
1818 Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
1819 switch (IntrinsicID) {
1820 case Intrinsic::vacopy: {
1821 unsigned PtrSize = ST->isTargetILP32() ? 4 : 8;
1822 unsigned VaListSize =
1823 (ST->isTargetDarwin() || ST->isTargetWindows())
1824 ? PtrSize
1825 : ST->isTargetILP32() ? 20 : 32;
1826
1827 MachineFunction &MF = *MI.getMF();
1829 LLT::scalar(VaListSize * 8));
1830 MIB.buildLoad(Val, MI.getOperand(2),
1833 VaListSize, Align(PtrSize)));
1834 MIB.buildStore(Val, MI.getOperand(1),
1837 VaListSize, Align(PtrSize)));
1838 MI.eraseFromParent();
1839 return true;
1840 }
1841 case Intrinsic::get_dynamic_area_offset: {
1842 MIB.buildConstant(MI.getOperand(0).getReg(), 0);
1843 MI.eraseFromParent();
1844 return true;
1845 }
1846 case Intrinsic::aarch64_mops_memset_tag: {
1847 assert(MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS);
1848 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
1849 // the instruction).
1850 auto &Value = MI.getOperand(3);
1851 Register ExtValueReg = MIB.buildAnyExt(LLT::integer(64), Value).getReg(0);
1852 Value.setReg(ExtValueReg);
1853 return true;
1854 }
1855 case Intrinsic::aarch64_prefetch: {
1856 auto &AddrVal = MI.getOperand(1);
1857
1858 int64_t IsWrite = MI.getOperand(2).getImm();
1859 int64_t Target = MI.getOperand(3).getImm();
1860 int64_t IsStream = MI.getOperand(4).getImm();
1861 int64_t IsData = MI.getOperand(5).getImm();
1862
1863 unsigned PrfOp = (IsWrite << 4) | // Load/Store bit
1864 (!IsData << 3) | // IsDataCache bit
1865 (Target << 1) | // Cache level bits
1866 (unsigned)IsStream; // Stream bit
1867
1868 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
1869 MI.eraseFromParent();
1870 return true;
1871 }
1872 case Intrinsic::aarch64_range_prefetch: {
1873 auto &AddrVal = MI.getOperand(1);
1874
1875 int64_t IsWrite = MI.getOperand(2).getImm();
1876 int64_t IsStream = MI.getOperand(3).getImm();
1877 unsigned PrfOp = (IsStream << 2) | IsWrite;
1878
1879 MIB.buildInstr(AArch64::G_AARCH64_RANGE_PREFETCH)
1880 .addImm(PrfOp)
1881 .add(AddrVal)
1882 .addUse(MI.getOperand(4).getReg()); // Metadata
1883 MI.eraseFromParent();
1884 return true;
1885 }
1886 case Intrinsic::aarch64_prefetch_ir: {
1887 auto &AddrVal = MI.getOperand(1);
1888 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(24).add(AddrVal);
1889 MI.eraseFromParent();
1890 return true;
1891 }
1892 case Intrinsic::aarch64_neon_uaddv:
1893 case Intrinsic::aarch64_neon_saddv:
1894 case Intrinsic::aarch64_neon_umaxv:
1895 case Intrinsic::aarch64_neon_smaxv:
1896 case Intrinsic::aarch64_neon_uminv:
1897 case Intrinsic::aarch64_neon_sminv: {
1898 bool IsSigned = IntrinsicID == Intrinsic::aarch64_neon_saddv ||
1899 IntrinsicID == Intrinsic::aarch64_neon_smaxv ||
1900 IntrinsicID == Intrinsic::aarch64_neon_sminv;
1901
1902 auto OldDst = MI.getOperand(0).getReg();
1903 auto OldDstTy = MRI.getType(OldDst);
1904 LLT NewDstTy = MRI.getType(MI.getOperand(2).getReg()).getElementType();
1905 if (OldDstTy == NewDstTy)
1906 return true;
1907
1908 auto NewDst = MRI.createGenericVirtualRegister(NewDstTy);
1909
1910 Helper.Observer.changingInstr(MI);
1911 MI.getOperand(0).setReg(NewDst);
1912 Helper.Observer.changedInstr(MI);
1913
1914 MIB.setInsertPt(MIB.getMBB(), ++MIB.getInsertPt());
1915 MIB.buildExtOrTrunc(IsSigned ? TargetOpcode::G_SEXT : TargetOpcode::G_ZEXT,
1916 OldDst, NewDst);
1917
1918 return true;
1919 }
1920 case Intrinsic::aarch64_neon_uaddlp:
1921 case Intrinsic::aarch64_neon_saddlp: {
1922 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlp
1923 ? AArch64::G_UADDLP
1924 : AArch64::G_SADDLP;
1925 MIB.buildInstr(Opc, {MI.getOperand(0)}, {MI.getOperand(2)});
1926 MI.eraseFromParent();
1927
1928 return true;
1929 }
1930 case Intrinsic::aarch64_neon_uaddlv:
1931 case Intrinsic::aarch64_neon_saddlv: {
1932 unsigned Opc = IntrinsicID == Intrinsic::aarch64_neon_uaddlv
1933 ? AArch64::G_UADDLV
1934 : AArch64::G_SADDLV;
1935 Register DstReg = MI.getOperand(0).getReg();
1936 Register SrcReg = MI.getOperand(2).getReg();
1937 LLT DstTy = MRI.getType(DstReg);
1938
1939 LLT MidTy, ExtTy;
1940 if (DstTy.isScalar() && DstTy.getScalarSizeInBits() <= 32) {
1941 ExtTy = LLT::integer(32);
1942 MidTy = LLT::fixed_vector(4, ExtTy);
1943 } else {
1944 ExtTy = LLT::integer(64);
1945 MidTy = LLT::fixed_vector(2, ExtTy);
1946 }
1947
1948 Register MidReg =
1949 MIB.buildInstr(Opc, {MidTy}, {SrcReg})->getOperand(0).getReg();
1950 Register ZeroReg =
1951 MIB.buildConstant(LLT::integer(64), 0)->getOperand(0).getReg();
1952 Register ExtReg = MIB.buildInstr(AArch64::G_EXTRACT_VECTOR_ELT, {ExtTy},
1953 {MidReg, ZeroReg})
1954 .getReg(0);
1955
1956 if (DstTy.getScalarSizeInBits() < 32)
1957 MIB.buildTrunc(DstReg, ExtReg);
1958 else
1959 MIB.buildCopy(DstReg, ExtReg);
1960
1961 MI.eraseFromParent();
1962
1963 return true;
1964 }
1965 case Intrinsic::aarch64_neon_smax:
1966 return LowerBinOp(TargetOpcode::G_SMAX);
1967 case Intrinsic::aarch64_neon_smin:
1968 return LowerBinOp(TargetOpcode::G_SMIN);
1969 case Intrinsic::aarch64_neon_umax:
1970 return LowerBinOp(TargetOpcode::G_UMAX);
1971 case Intrinsic::aarch64_neon_umin:
1972 return LowerBinOp(TargetOpcode::G_UMIN);
1973 case Intrinsic::aarch64_neon_fmax:
1974 return LowerBinOp(TargetOpcode::G_FMAXIMUM);
1975 case Intrinsic::aarch64_neon_fmin:
1976 return LowerBinOp(TargetOpcode::G_FMINIMUM);
1977 case Intrinsic::aarch64_neon_fmaxnm:
1978 return LowerBinOp(TargetOpcode::G_FMAXNUM);
1979 case Intrinsic::aarch64_neon_fminnm:
1980 return LowerBinOp(TargetOpcode::G_FMINNUM);
1981 case Intrinsic::aarch64_neon_pmul:
1982 return LowerBinOp(TargetOpcode::G_CLMUL);
1983 case Intrinsic::aarch64_neon_pmull:
1984 case Intrinsic::aarch64_neon_pmull64:
1985 return LowerBinOp(AArch64::G_PMULL);
1986 case Intrinsic::aarch64_neon_smull:
1987 return LowerBinOp(AArch64::G_SMULL);
1988 case Intrinsic::aarch64_neon_umull:
1989 return LowerBinOp(AArch64::G_UMULL);
1990 case Intrinsic::aarch64_neon_sabd:
1991 return LowerBinOp(TargetOpcode::G_ABDS);
1992 case Intrinsic::aarch64_neon_uabd:
1993 return LowerBinOp(TargetOpcode::G_ABDU);
1994 case Intrinsic::aarch64_neon_uhadd:
1995 return LowerBinOp(TargetOpcode::G_UAVGFLOOR);
1996 case Intrinsic::aarch64_neon_urhadd:
1997 return LowerBinOp(TargetOpcode::G_UAVGCEIL);
1998 case Intrinsic::aarch64_neon_shadd:
1999 return LowerBinOp(TargetOpcode::G_SAVGFLOOR);
2000 case Intrinsic::aarch64_neon_srhadd:
2001 return LowerBinOp(TargetOpcode::G_SAVGCEIL);
2002 case Intrinsic::aarch64_neon_sqshrn: {
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_VASHR,
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_sqshrun: {
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_VASHR,
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_sqrshrn: {
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_SRSHR_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_SSAT_S, {MI.getOperand(0)}, {Shr});
2035 MI.eraseFromParent();
2036 return true;
2037 }
2038 case Intrinsic::aarch64_neon_sqrshrun: {
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_SRSHR_I,
2043 {MRI.getType(MI.getOperand(2).getReg())},
2044 {MI.getOperand(2), MI.getOperand(3).getImm()});
2045 // Build the narrow intrinsic, taking in Shr.
2046 MIB.buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {MI.getOperand(0)}, {Shr});
2047 MI.eraseFromParent();
2048 return true;
2049 }
2050 case Intrinsic::aarch64_neon_uqrshrn: {
2051 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2052 return true;
2053 // Create right shift instruction. Store the output register in Shr.
2054 auto Shr = MIB.buildInstr(AArch64::G_URSHR_I,
2055 {MRI.getType(MI.getOperand(2).getReg())},
2056 {MI.getOperand(2), MI.getOperand(3).getImm()});
2057 // Build the narrow intrinsic, taking in Shr.
2058 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2059 MI.eraseFromParent();
2060 return true;
2061 }
2062 case Intrinsic::aarch64_neon_uqshrn: {
2063 if (!MRI.getType(MI.getOperand(0).getReg()).isVector())
2064 return true;
2065 // Create right shift instruction. Store the output register in Shr.
2066 auto Shr = MIB.buildInstr(AArch64::G_VLSHR,
2067 {MRI.getType(MI.getOperand(2).getReg())},
2068 {MI.getOperand(2), MI.getOperand(3).getImm()});
2069 // Build the narrow intrinsic, taking in Shr.
2070 MIB.buildInstr(TargetOpcode::G_TRUNC_USAT_U, {MI.getOperand(0)}, {Shr});
2071 MI.eraseFromParent();
2072 return true;
2073 }
2074 case Intrinsic::aarch64_neon_sqshlu: {
2075 // Check if last operand is constant vector dup
2076 auto ShiftAmount =
2077 isConstantOrConstantSplatVector(MI.getOperand(3).getReg(), MRI);
2078 if (ShiftAmount) {
2079 // If so, create a new intrinsic with the correct shift amount
2080 MIB.buildInstr(AArch64::G_SQSHLU_I, {MI.getOperand(0)},
2081 {MI.getOperand(2)})
2082 .addImm(ShiftAmount->getSExtValue());
2083 MI.eraseFromParent();
2084 return true;
2085 }
2086 return false;
2087 }
2088 case Intrinsic::aarch64_neon_vsli: {
2089 MIB.buildInstr(
2090 AArch64::G_SLI, {MI.getOperand(0)},
2091 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2092 MI.eraseFromParent();
2093 break;
2094 }
2095 case Intrinsic::aarch64_neon_vsri: {
2096 MIB.buildInstr(
2097 AArch64::G_SRI, {MI.getOperand(0)},
2098 {MI.getOperand(2), MI.getOperand(3), MI.getOperand(4).getImm()});
2099 MI.eraseFromParent();
2100 break;
2101 }
2102 case Intrinsic::aarch64_neon_abs: {
2103 // Lower the intrinsic to G_ABS.
2104 MIB.buildInstr(TargetOpcode::G_ABS, {MI.getOperand(0)}, {MI.getOperand(2)});
2105 MI.eraseFromParent();
2106 return true;
2107 }
2108 case Intrinsic::aarch64_neon_addhn:
2109 return LowerBinOp(AArch64::G_ADDHN);
2110 case Intrinsic::aarch64_neon_sqadd: {
2111 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2112 return LowerBinOp(TargetOpcode::G_SADDSAT);
2113 break;
2114 }
2115 case Intrinsic::aarch64_neon_sqsub: {
2116 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2117 return LowerBinOp(TargetOpcode::G_SSUBSAT);
2118 break;
2119 }
2120 case Intrinsic::aarch64_neon_uqadd: {
2121 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2122 return LowerBinOp(TargetOpcode::G_UADDSAT);
2123 break;
2124 }
2125 case Intrinsic::aarch64_neon_uqsub: {
2126 if (MRI.getType(MI.getOperand(0).getReg()).isVector())
2127 return LowerBinOp(TargetOpcode::G_USUBSAT);
2128 break;
2129 }
2130 case Intrinsic::aarch64_neon_udot:
2131 return LowerTriOp(AArch64::G_UDOT);
2132 case Intrinsic::aarch64_neon_sdot:
2133 return LowerTriOp(AArch64::G_SDOT);
2134 case Intrinsic::aarch64_neon_usdot:
2135 return LowerTriOp(AArch64::G_USDOT);
2136 case Intrinsic::aarch64_neon_sqxtn:
2137 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_S);
2138 case Intrinsic::aarch64_neon_sqxtun:
2139 return LowerUnaryOp(TargetOpcode::G_TRUNC_SSAT_U);
2140 case Intrinsic::aarch64_neon_uqxtn:
2141 return LowerUnaryOp(TargetOpcode::G_TRUNC_USAT_U);
2142 case Intrinsic::aarch64_neon_fcvtzu:
2143 return LowerUnaryOp(TargetOpcode::G_FPTOUI_SAT);
2144 case Intrinsic::aarch64_neon_fcvtzs:
2145 return LowerUnaryOp(TargetOpcode::G_FPTOSI_SAT);
2146 case Intrinsic::aarch64_neon_cls:
2147 return LowerUnaryOp(TargetOpcode::G_CTLS);
2148
2149 case Intrinsic::vector_reverse:
2150 // TODO: Add support for vector_reverse
2151 return false;
2152 }
2153
2154 return true;
2155}
2156
2157bool AArch64LegalizerInfo::legalizeShlAshrLshr(
2159 GISelChangeObserver &Observer) const {
2160 assert(MI.getOpcode() == TargetOpcode::G_ASHR ||
2161 MI.getOpcode() == TargetOpcode::G_LSHR ||
2162 MI.getOpcode() == TargetOpcode::G_SHL);
2163 // If the shift amount is a G_CONSTANT, promote it to a 64 bit type so the
2164 // imported patterns can select it later. Either way, it will be legal.
2165 Register AmtReg = MI.getOperand(2).getReg();
2166 LLT AmtRegEltTy = MRI.getType(AmtReg).getScalarType();
2167 auto VRegAndVal = getIConstantVRegValWithLookThrough(AmtReg, MRI);
2168 if (!VRegAndVal)
2169 return true;
2170 // Check the shift amount is in range for an immediate form.
2171 int64_t Amount = VRegAndVal->Value.getSExtValue();
2172 if (Amount > 31)
2173 return true; // This will have to remain a register variant.
2174 auto ExtCst =
2175 MIRBuilder.buildConstant(AmtRegEltTy.changeElementSize(64), Amount);
2176 Observer.changingInstr(MI);
2177 MI.getOperand(2).setReg(ExtCst.getReg(0));
2178 Observer.changedInstr(MI);
2179 return true;
2180}
2181
2183 MachineRegisterInfo &MRI) {
2184 Base = Root;
2185 Offset = 0;
2186
2187 Register NewBase;
2188 int64_t NewOffset;
2189 if (mi_match(Root, MRI, m_GPtrAdd(m_Reg(NewBase), m_ICst(NewOffset))) &&
2190 isShiftedInt<7, 3>(NewOffset)) {
2191 Base = NewBase;
2192 Offset = NewOffset;
2193 }
2194}
2195
2196// FIXME: This should be removed and replaced with the generic bitcast legalize
2197// action.
2198bool AArch64LegalizerInfo::legalizeLoadStore(
2200 GISelChangeObserver &Observer) const {
2201 assert(MI.getOpcode() == TargetOpcode::G_STORE ||
2202 MI.getOpcode() == TargetOpcode::G_LOAD);
2203 // Here we just try to handle vector loads/stores where our value type might
2204 // have pointer elements, which the SelectionDAG importer can't handle. To
2205 // allow the existing patterns for s64 to fire for p0, we just try to bitcast
2206 // the value to use s64 types.
2207
2208 // Custom legalization requires the instruction, if not deleted, must be fully
2209 // legalized. In order to allow further legalization of the inst, we create
2210 // a new instruction and erase the existing one.
2211
2212 Register ValReg = MI.getOperand(0).getReg();
2213 const LLT ValTy = MRI.getType(ValReg);
2214
2215 if (ValTy == LLT::scalar(128)) {
2216
2217 AtomicOrdering Ordering = (*MI.memoperands_begin())->getSuccessOrdering();
2218 bool IsLoad = MI.getOpcode() == TargetOpcode::G_LOAD;
2219 bool IsLoadAcquire = IsLoad && Ordering == AtomicOrdering::Acquire;
2220 bool IsStoreRelease = !IsLoad && Ordering == AtomicOrdering::Release;
2221 bool IsRcpC3 =
2222 ST->hasLSE2() && ST->hasRCPC3() && (IsLoadAcquire || IsStoreRelease);
2223
2224 LLT s64 = LLT::integer(64);
2225
2226 unsigned Opcode;
2227 if (IsRcpC3) {
2228 Opcode = IsLoad ? AArch64::LDIAPPX : AArch64::STILPX;
2229 } else {
2230 // For LSE2, loads/stores should have been converted to monotonic and had
2231 // a fence inserted after them.
2232 assert(Ordering == AtomicOrdering::Monotonic ||
2233 Ordering == AtomicOrdering::Unordered);
2234 assert(ST->hasLSE2() && "ldp/stp not single copy atomic without +lse2");
2235
2236 Opcode = IsLoad ? AArch64::LDPXi : AArch64::STPXi;
2237 }
2238
2239 MachineInstrBuilder NewI;
2240 if (IsLoad) {
2241 NewI = MIRBuilder.buildInstr(Opcode, {s64, s64}, {});
2242 MIRBuilder.buildMergeLikeInstr(
2243 ValReg, {NewI->getOperand(0), NewI->getOperand(1)});
2244 } else {
2245 auto Split = MIRBuilder.buildUnmerge(s64, MI.getOperand(0));
2246 NewI = MIRBuilder.buildInstr(
2247 Opcode, {}, {Split->getOperand(0), Split->getOperand(1)});
2248 }
2249
2250 if (IsRcpC3) {
2251 NewI.addUse(MI.getOperand(1).getReg());
2252 } else {
2253 Register Base;
2254 int Offset;
2255 matchLDPSTPAddrMode(MI.getOperand(1).getReg(), Base, Offset, MRI);
2256 NewI.addUse(Base);
2257 NewI.addImm(Offset / 8);
2258 }
2259
2260 NewI.cloneMemRefs(MI);
2261 constrainSelectedInstRegOperands(*NewI, *ST->getInstrInfo(),
2262 *MRI.getTargetRegisterInfo(),
2263 *ST->getRegBankInfo());
2264 MI.eraseFromParent();
2265 return true;
2266 }
2267
2268 if (!ValTy.isPointerVector() ||
2269 ValTy.getElementType().getAddressSpace() != 0) {
2270 LLVM_DEBUG(dbgs() << "Tried to do custom legalization on wrong load/store");
2271 return false;
2272 }
2273
2274 unsigned PtrSize = ValTy.getElementType().getSizeInBits();
2275 const LLT NewTy = LLT::vector(ValTy.getElementCount(), LLT::integer(PtrSize));
2276 auto &MMO = **MI.memoperands_begin();
2277 MMO.setType(NewTy);
2278
2279 if (MI.getOpcode() == TargetOpcode::G_STORE) {
2280 auto Bitcast = MIRBuilder.buildBitcast(NewTy, ValReg);
2281 MIRBuilder.buildStore(Bitcast.getReg(0), MI.getOperand(1), MMO);
2282 } else {
2283 auto NewLoad = MIRBuilder.buildLoad(NewTy, MI.getOperand(1), MMO);
2284 MIRBuilder.buildBitcast(ValReg, NewLoad);
2285 }
2286 MI.eraseFromParent();
2287 return true;
2288}
2289
2290bool AArch64LegalizerInfo::legalizeVaArg(MachineInstr &MI,
2292 MachineIRBuilder &MIRBuilder) const {
2293 MachineFunction &MF = MIRBuilder.getMF();
2294 Align Alignment(MI.getOperand(2).getImm());
2295 Register Dst = MI.getOperand(0).getReg();
2296 Register ListPtr = MI.getOperand(1).getReg();
2297
2298 LLT PtrTy = MRI.getType(ListPtr);
2299 LLT IntPtrTy = LLT::integer(PtrTy.getSizeInBits());
2300
2301 const unsigned PtrSize = PtrTy.getSizeInBits() / 8;
2302 const Align PtrAlign = Align(PtrSize);
2303 auto List = MIRBuilder.buildLoad(
2304 PtrTy, ListPtr,
2305 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2306 PtrTy, PtrAlign));
2307
2308 MachineInstrBuilder DstPtr;
2309 if (Alignment > PtrAlign) {
2310 // Realign the list to the actual required alignment.
2311 auto AlignMinus1 =
2312 MIRBuilder.buildConstant(IntPtrTy, Alignment.value() - 1);
2313 auto ListTmp = MIRBuilder.buildPtrAdd(PtrTy, List, AlignMinus1.getReg(0));
2314 DstPtr = MIRBuilder.buildMaskLowPtrBits(PtrTy, ListTmp, Log2(Alignment));
2315 } else
2316 DstPtr = List;
2317
2318 LLT ValTy = MRI.getType(Dst);
2319 uint64_t ValSize = ValTy.getSizeInBits() / 8;
2320 MIRBuilder.buildLoad(
2321 Dst, DstPtr,
2322 *MF.getMachineMemOperand(MachinePointerInfo(), MachineMemOperand::MOLoad,
2323 ValTy, std::max(Alignment, PtrAlign)));
2324
2325 auto Size = MIRBuilder.buildConstant(IntPtrTy, alignTo(ValSize, PtrAlign));
2326
2327 auto NewList = MIRBuilder.buildPtrAdd(PtrTy, DstPtr, Size.getReg(0));
2328
2329 MIRBuilder.buildStore(NewList, ListPtr,
2330 *MF.getMachineMemOperand(MachinePointerInfo(),
2332 PtrTy, PtrAlign));
2333
2334 MI.eraseFromParent();
2335 return true;
2336}
2337
2338bool AArch64LegalizerInfo::legalizeBitfieldExtract(
2339 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2340 // Only legal if we can select immediate forms.
2341 // TODO: Lower this otherwise.
2342 return getIConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI) &&
2343 getIConstantVRegValWithLookThrough(MI.getOperand(3).getReg(), MRI);
2344}
2345
2346bool AArch64LegalizerInfo::legalizeCTPOP(MachineInstr &MI,
2348 LegalizerHelper &Helper) const {
2349 // When there is no integer popcount instruction (FEAT_CSSC isn't available),
2350 // it can be more efficiently lowered to the following sequence that uses
2351 // AdvSIMD registers/instructions as long as the copies to/from the AdvSIMD
2352 // registers are cheap.
2353 // FMOV D0, X0 // copy 64-bit int to vector, high bits zero'd
2354 // CNT V0.8B, V0.8B // 8xbyte pop-counts
2355 // ADDV B0, V0.8B // sum 8xbyte pop-counts
2356 // UMOV X0, V0.B[0] // copy byte result back to integer reg
2357 //
2358 // For 128 bit vector popcounts, we lower to the following sequence:
2359 // cnt.16b v0, v0 // v8s16, v4s32, v2s64
2360 // uaddlp.8h v0, v0 // v8s16, v4s32, v2s64
2361 // uaddlp.4s v0, v0 // v4s32, v2s64
2362 // uaddlp.2d v0, v0 // v2s64
2363 //
2364 // For 64 bit vector popcounts, we lower to the following sequence:
2365 // cnt.8b v0, v0 // v4s16, v2s32
2366 // uaddlp.4h v0, v0 // v4s16, v2s32
2367 // uaddlp.2s v0, v0 // v2s32
2368
2369 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2370 Register Dst = MI.getOperand(0).getReg();
2371 Register Val = MI.getOperand(1).getReg();
2372 LLT Ty = MRI.getType(Val);
2373
2374 LLT i64 = LLT::integer(64);
2375 LLT i32 = LLT::integer(32);
2376 LLT i16 = LLT::integer(16);
2377 LLT i8 = LLT::integer(8);
2378 unsigned Size = Ty.getSizeInBits();
2379
2380 assert(Ty == MRI.getType(Dst) &&
2381 "Expected src and dst to have the same type!");
2382
2383 if (ST->hasCSSC() && Ty.isScalar() && Size == 128) {
2384
2385 auto Split = MIRBuilder.buildUnmerge(i64, Val);
2386 auto CTPOP1 = MIRBuilder.buildCTPOP(i64, Split->getOperand(0));
2387 auto CTPOP2 = MIRBuilder.buildCTPOP(i64, Split->getOperand(1));
2388 auto Add = MIRBuilder.buildAdd(i64, CTPOP1, CTPOP2);
2389
2390 MIRBuilder.buildZExt(Dst, Add);
2391 MI.eraseFromParent();
2392 return true;
2393 }
2394
2395 if (!ST->hasNEON() ||
2396 MI.getMF()->getFunction().hasFnAttribute(Attribute::NoImplicitFloat)) {
2397 // Use generic lowering when custom lowering is not possible.
2398 return Ty.isScalar() && (Size == 32 || Size == 64) &&
2399 Helper.lowerBitCount(MI) ==
2401 }
2402
2403 // Pre-conditioning: widen Val up to the nearest vector type.
2404 // s32,s64,v4s16,v2s32 -> v8i8
2405 // v8s16,v4s32,v2s64 -> v16i8
2406 LLT VTy = Size == 128 ? LLT::fixed_vector(16, i8) : LLT::fixed_vector(8, i8);
2407 if (Ty.isScalar()) {
2408 assert((Size == 32 || Size == 64 || Size == 128) && "Expected only 32, 64, or 128 bit scalars!");
2409 if (Size == 32) {
2410 Val = MIRBuilder.buildZExt(i64, Val).getReg(0);
2411 }
2412 }
2413 Val = MIRBuilder.buildBitcast(VTy, Val).getReg(0);
2414
2415 // Count bits in each byte-sized lane.
2416 auto CTPOP = MIRBuilder.buildCTPOP(VTy, Val);
2417
2418 // Sum across lanes.
2419 if (ST->hasDotProd() && Ty.isVector() && Ty.getNumElements() >= 2 &&
2420 Ty.getScalarSizeInBits() != 16) {
2421 LLT Dt = Ty == LLT::fixed_vector(2, i64) ? LLT::fixed_vector(4, i32) : Ty;
2422 auto Zeros = MIRBuilder.buildConstant(Dt, 0);
2423 auto Ones = MIRBuilder.buildConstant(VTy, 1);
2424 MachineInstrBuilder Sum;
2425
2426 if (Ty == LLT::fixed_vector(2, i64)) {
2427 auto UDOT =
2428 MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2429 Sum = MIRBuilder.buildInstr(AArch64::G_UADDLP, {Ty}, {UDOT});
2430 } else if (Ty == LLT::fixed_vector(4, i32)) {
2431 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2432 } else if (Ty == LLT::fixed_vector(2, i32)) {
2433 Sum = MIRBuilder.buildInstr(AArch64::G_UDOT, {Dt}, {Zeros, Ones, CTPOP});
2434 } else {
2435 llvm_unreachable("unexpected vector shape");
2436 }
2437
2438 Sum->getOperand(0).setReg(Dst);
2439 MI.eraseFromParent();
2440 return true;
2441 }
2442
2443 Register HSum = CTPOP.getReg(0);
2444 unsigned Opc;
2445 SmallVector<LLT> HAddTys;
2446 if (Ty.isScalar()) {
2447 Opc = Intrinsic::aarch64_neon_uaddlv;
2448 HAddTys.push_back(i32);
2449 } else if (Ty == LLT::fixed_vector(8, i16)) {
2450 Opc = Intrinsic::aarch64_neon_uaddlp;
2451 HAddTys.push_back(LLT::fixed_vector(8, i16));
2452 } else if (Ty == LLT::fixed_vector(4, i32)) {
2453 Opc = Intrinsic::aarch64_neon_uaddlp;
2454 HAddTys.push_back(LLT::fixed_vector(8, i16));
2455 HAddTys.push_back(LLT::fixed_vector(4, i32));
2456 } else if (Ty == LLT::fixed_vector(2, i64)) {
2457 Opc = Intrinsic::aarch64_neon_uaddlp;
2458 HAddTys.push_back(LLT::fixed_vector(8, i16));
2459 HAddTys.push_back(LLT::fixed_vector(4, i32));
2460 HAddTys.push_back(LLT::fixed_vector(2, i64));
2461 } else if (Ty == LLT::fixed_vector(4, i16)) {
2462 Opc = Intrinsic::aarch64_neon_uaddlp;
2463 HAddTys.push_back(LLT::fixed_vector(4, i16));
2464 } else if (Ty == LLT::fixed_vector(2, i32)) {
2465 Opc = Intrinsic::aarch64_neon_uaddlp;
2466 HAddTys.push_back(LLT::fixed_vector(4, i16));
2467 HAddTys.push_back(LLT::fixed_vector(2, i32));
2468 } else
2469 llvm_unreachable("unexpected vector shape");
2471 for (LLT HTy : HAddTys) {
2472 UADD = MIRBuilder.buildIntrinsic(Opc, {HTy}).addUse(HSum);
2473 HSum = UADD.getReg(0);
2474 }
2475
2476 // Post-conditioning.
2477 if (Ty.isScalar() && (Size == 64 || Size == 128))
2478 MIRBuilder.buildZExt(Dst, UADD);
2479 else
2480 UADD->getOperand(0).setReg(Dst);
2481 MI.eraseFromParent();
2482 return true;
2483}
2484
2485bool AArch64LegalizerInfo::legalizeAtomicCmpxchg128(
2486 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2487 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2488 LLT i64 = LLT::integer(64);
2489 auto Addr = MI.getOperand(1).getReg();
2490 auto DesiredI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(2));
2491 auto NewI = MIRBuilder.buildUnmerge({i64, i64}, MI.getOperand(3));
2492 auto DstLo = MRI.createGenericVirtualRegister(i64);
2493 auto DstHi = MRI.createGenericVirtualRegister(i64);
2494
2495 MachineInstrBuilder CAS;
2496 if (ST->hasLSE()) {
2497 // We have 128-bit CASP instructions taking XSeqPair registers, which are
2498 // s128. We need the merge/unmerge to bracket the expansion and pair up with
2499 // the rest of the MIR so we must reassemble the extracted registers into a
2500 // 128-bit known-regclass one with code like this:
2501 //
2502 // %in1 = REG_SEQUENCE Lo, Hi ; One for each input
2503 // %out = CASP %in1, ...
2504 // %OldLo = G_EXTRACT %out, 0
2505 // %OldHi = G_EXTRACT %out, 64
2506 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2507 unsigned Opcode;
2508 switch (Ordering) {
2510 Opcode = AArch64::CASPAX;
2511 break;
2513 Opcode = AArch64::CASPLX;
2514 break;
2517 Opcode = AArch64::CASPALX;
2518 break;
2519 default:
2520 Opcode = AArch64::CASPX;
2521 break;
2522 }
2523
2524 LLT s128 = LLT::scalar(128);
2525 auto CASDst = MRI.createGenericVirtualRegister(s128);
2526 auto CASDesired = MRI.createGenericVirtualRegister(s128);
2527 auto CASNew = MRI.createGenericVirtualRegister(s128);
2528 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASDesired}, {})
2529 .addUse(DesiredI->getOperand(0).getReg())
2530 .addImm(AArch64::sube64)
2531 .addUse(DesiredI->getOperand(1).getReg())
2532 .addImm(AArch64::subo64);
2533 MIRBuilder.buildInstr(TargetOpcode::REG_SEQUENCE, {CASNew}, {})
2534 .addUse(NewI->getOperand(0).getReg())
2535 .addImm(AArch64::sube64)
2536 .addUse(NewI->getOperand(1).getReg())
2537 .addImm(AArch64::subo64);
2538
2539 CAS = MIRBuilder.buildInstr(Opcode, {CASDst}, {CASDesired, CASNew, Addr});
2540
2541 MIRBuilder.buildExtract({DstLo}, {CASDst}, 0);
2542 MIRBuilder.buildExtract({DstHi}, {CASDst}, 64);
2543 } else {
2544 // The -O0 CMP_SWAP_128 is friendlier to generate code for because LDXP/STXP
2545 // can take arbitrary registers so it just has the normal GPR64 operands the
2546 // rest of AArch64 is expecting.
2547 auto Ordering = (*MI.memoperands_begin())->getMergedOrdering();
2548 unsigned Opcode;
2549 switch (Ordering) {
2551 Opcode = AArch64::CMP_SWAP_128_ACQUIRE;
2552 break;
2554 Opcode = AArch64::CMP_SWAP_128_RELEASE;
2555 break;
2558 Opcode = AArch64::CMP_SWAP_128;
2559 break;
2560 default:
2561 Opcode = AArch64::CMP_SWAP_128_MONOTONIC;
2562 break;
2563 }
2564
2565 auto Scratch = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
2566 CAS = MIRBuilder.buildInstr(Opcode, {DstLo, DstHi, Scratch},
2567 {Addr, DesiredI->getOperand(0),
2568 DesiredI->getOperand(1), NewI->getOperand(0),
2569 NewI->getOperand(1)});
2570 }
2571
2572 CAS.cloneMemRefs(MI);
2573 constrainSelectedInstRegOperands(*CAS, *ST->getInstrInfo(),
2574 *MRI.getTargetRegisterInfo(),
2575 *ST->getRegBankInfo());
2576
2577 MIRBuilder.buildMergeLikeInstr(MI.getOperand(0), {DstLo, DstHi});
2578 MI.eraseFromParent();
2579 return true;
2580}
2581
2582bool AArch64LegalizerInfo::legalizeCTTZ(MachineInstr &MI,
2583 LegalizerHelper &Helper) const {
2584 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2585 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2586 LLT Ty = MRI.getType(MI.getOperand(1).getReg());
2587 auto BitReverse = MIRBuilder.buildBitReverse(Ty, MI.getOperand(1));
2588 MIRBuilder.buildCTLZ(MI.getOperand(0).getReg(), BitReverse);
2589 MI.eraseFromParent();
2590 return true;
2591}
2592
2593bool AArch64LegalizerInfo::legalizeMemOps(MachineInstr &MI,
2594 LegalizerHelper &Helper) const {
2595 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2596
2597 // Tagged version MOPSMemorySetTagged is legalised in legalizeIntrinsic
2598 if (MI.getOpcode() == TargetOpcode::G_MEMSET ||
2599 MI.getOpcode() == TargetOpcode::G_MEMSET_INLINE) {
2600 // Anyext the value being set to 64 bit (only the bottom 8 bits are read by
2601 // the instruction).
2602 auto &Value = MI.getOperand(1);
2603 Register ExtValueReg =
2604 MIRBuilder.buildAnyExt(LLT::integer(64), Value).getReg(0);
2605 Value.setReg(ExtValueReg);
2606 return true;
2607 }
2608
2609 return false;
2610}
2611
2612bool AArch64LegalizerInfo::legalizeExtractVectorElt(
2613 MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const {
2614 const GExtractVectorElement *Element = cast<GExtractVectorElement>(&MI);
2615 auto VRegAndVal =
2617 if (VRegAndVal)
2618 return true;
2619 LLT VecTy = MRI.getType(Element->getVectorReg());
2620 if (VecTy.isScalableVector())
2621 return true;
2622 return Helper.lowerExtractInsertVectorElt(MI) !=
2624}
2625
2626bool AArch64LegalizerInfo::legalizeDynStackAlloc(
2627 MachineInstr &MI, LegalizerHelper &Helper) const {
2628 MachineFunction &MF = *MI.getParent()->getParent();
2629 MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
2630 MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
2631
2632 // If stack probing is not enabled for this function, use the default
2633 // lowering.
2634 if (!MF.getFunction().hasFnAttribute("probe-stack") ||
2635 MF.getFunction().getFnAttribute("probe-stack").getValueAsString() !=
2636 "inline-asm") {
2637 Helper.lowerDynStackAlloc(MI);
2638 return true;
2639 }
2640
2641 Register Dst = MI.getOperand(0).getReg();
2642 Register AllocSize = MI.getOperand(1).getReg();
2643 Align Alignment = assumeAligned(MI.getOperand(2).getImm());
2644
2645 assert(MRI.getType(Dst) == LLT::pointer(0, 64) &&
2646 "Unexpected type for dynamic alloca");
2647 assert(MRI.getType(AllocSize) == LLT::scalar(64) &&
2648 "Unexpected type for dynamic alloca");
2649
2650 LLT PtrTy = MRI.getType(Dst);
2651 Register SPReg =
2653 Register SPTmp =
2654 Helper.getDynStackAllocTargetPtr(SPReg, AllocSize, Alignment, PtrTy);
2655 auto NewMI =
2656 MIRBuilder.buildInstr(AArch64::PROBED_STACKALLOC_DYN, {}, {SPTmp});
2657 MRI.setRegClass(NewMI.getReg(0), &AArch64::GPR64commonRegClass);
2658 MIRBuilder.setInsertPt(*NewMI->getParent(), NewMI);
2659 MIRBuilder.buildCopy(Dst, SPTmp);
2660
2661 MI.eraseFromParent();
2662 return true;
2663}
2664
2665bool AArch64LegalizerInfo::legalizePrefetch(MachineInstr &MI,
2666 LegalizerHelper &Helper) const {
2667 MachineIRBuilder &MIB = Helper.MIRBuilder;
2668 auto &AddrVal = MI.getOperand(0);
2669
2670 int64_t IsWrite = MI.getOperand(1).getImm();
2671 int64_t Locality = MI.getOperand(2).getImm();
2672 int64_t IsData = MI.getOperand(3).getImm();
2673
2674 bool IsStream = Locality == 0;
2675 if (Locality != 0) {
2676 assert(Locality <= 3 && "Prefetch locality out-of-range");
2677 // The locality degree is the opposite of the cache speed.
2678 // Put the number the other way around.
2679 // The encoding starts at 0 for level 1
2680 Locality = 3 - Locality;
2681 }
2682
2683 unsigned PrfOp = (IsWrite << 4) | (!IsData << 3) | (Locality << 1) | IsStream;
2684
2685 MIB.buildInstr(AArch64::G_AARCH64_PREFETCH).addImm(PrfOp).add(AddrVal);
2686 MI.eraseFromParent();
2687 return true;
2688}
2689
2690bool AArch64LegalizerInfo::legalizeConcatVectors(
2692 MachineIRBuilder &MIRBuilder) const {
2693 // Widen sub-byte element vectors to byte-sized elements before concatenating.
2694 // This is analogous to SDAG's integer type promotion for sub-byte types.
2696 Register DstReg = Concat.getReg(0);
2697 LLT DstTy = MRI.getType(DstReg);
2698 assert(DstTy.getScalarSizeInBits() < 8 && "Expected dst ty to be < 8b");
2699
2700 unsigned WideEltSize =
2701 std::max(8u, (unsigned)PowerOf2Ceil(DstTy.getScalarSizeInBits()));
2702 LLT SrcTy = MRI.getType(Concat.getSourceReg(0));
2703 LLT WideSrcTy = SrcTy.changeElementSize(WideEltSize);
2704 LLT WideDstTy = DstTy.changeElementSize(WideEltSize);
2705
2706 SmallVector<Register> WideSrcs;
2707 for (unsigned I = 0; I < Concat.getNumSources(); ++I) {
2708 auto Wide = MIRBuilder.buildAnyExt(WideSrcTy, Concat.getSourceReg(I));
2709 WideSrcs.push_back(Wide.getReg(0));
2710 }
2711
2712 auto WideConcat = MIRBuilder.buildConcatVectors(WideDstTy, WideSrcs);
2713 MIRBuilder.buildTrunc(DstReg, WideConcat);
2714 MI.eraseFromParent();
2715 return true;
2716}
2717
2718bool AArch64LegalizerInfo::legalizeFptrunc(MachineInstr &MI,
2719 MachineIRBuilder &MIRBuilder,
2720 MachineRegisterInfo &MRI) const {
2721 auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
2722
2723 // This function legalizes f64 -> bf16 and f64 -> f16 truncations via f64 ->
2724 // f32 G_FPTRUNC_ODD and f32 -> [b]f16 G_FPTRUNC, which apparently avoids the
2725 // usual double-rounding issue that could be present from using twin
2726 // G_FPTRUNC.
2727
2728 if (DstTy.isBFloat16() && SrcTy.isFloat64()) {
2729 auto Mid = MIRBuilder.buildInstr(AArch64::G_FPTRUNC_ODD, {LLT::float32()},
2730 {Src}, MI.getFlags());
2731 MIRBuilder.buildInstr(AArch64::G_FPTRUNC, {Dst}, {Mid}, MI.getFlags());
2732 MI.eraseFromParent();
2733 return true;
2734 }
2735
2736 assert(SrcTy.isFixedVector() && isPowerOf2_32(SrcTy.getNumElements()) &&
2737 "Expected a power of 2 elements");
2738
2739 // We must mutate types here as FPTrunc may be used on a IEEE floating point
2740 // or a brainfloat.
2741 LLT v2s16 = DstTy.changeElementCount(2);
2742 LLT v4s16 = DstTy.changeElementCount(4);
2743 LLT v2s32 = SrcTy.changeElementCount(2).changeElementSize(32);
2744 LLT v4s32 = SrcTy.changeElementCount(4).changeElementSize(32);
2745 LLT v2s64 = SrcTy.changeElementCount(2);
2746
2747 SmallVector<Register> RegsToUnmergeTo;
2748 SmallVector<Register> TruncOddDstRegs;
2749 SmallVector<Register> RegsToMerge;
2750
2751 unsigned ElemCount = SrcTy.getNumElements();
2752
2753 // Find the biggest size chunks we can work with
2754 int StepSize = ElemCount % 4 ? 2 : 4;
2755
2756 // If we have a power of 2 greater than 2, we need to first unmerge into
2757 // enough pieces
2758 if (ElemCount <= 2)
2759 RegsToUnmergeTo.push_back(Src);
2760 else {
2761 for (unsigned i = 0; i < ElemCount / 2; ++i)
2762 RegsToUnmergeTo.push_back(MRI.createGenericVirtualRegister(v2s64));
2763
2764 MIRBuilder.buildUnmerge(RegsToUnmergeTo, Src);
2765 }
2766
2767 // Create all of the round-to-odd instructions and store them
2768 for (auto SrcReg : RegsToUnmergeTo) {
2769 Register Mid = MIRBuilder
2770 .buildInstr(AArch64::G_FPTRUNC_ODD, {v2s32}, {SrcReg},
2771 MI.getFlags())
2772 .getReg(0);
2773 TruncOddDstRegs.push_back(Mid);
2774 }
2775
2776 // Truncate 4s32 to 4s16 if we can to reduce instruction count, otherwise
2777 // truncate 2s32 to 2s16.
2778 unsigned Index = 0;
2779 for (unsigned LoopIter = 0; LoopIter < ElemCount / StepSize; ++LoopIter) {
2780 if (StepSize == 4) {
2781 Register ConcatDst =
2782 MIRBuilder
2784 {v4s32}, {TruncOddDstRegs[Index++], TruncOddDstRegs[Index++]})
2785 .getReg(0);
2786
2787 RegsToMerge.push_back(
2788 MIRBuilder.buildFPTrunc(v4s16, ConcatDst, MI.getFlags()).getReg(0));
2789 } else {
2790 RegsToMerge.push_back(
2791 MIRBuilder
2792 .buildFPTrunc(v2s16, TruncOddDstRegs[Index++], MI.getFlags())
2793 .getReg(0));
2794 }
2795 }
2796
2797 // If there is only one register, replace the destination
2798 if (RegsToMerge.size() == 1) {
2799 MRI.replaceRegWith(Dst, RegsToMerge.pop_back_val());
2800 MI.eraseFromParent();
2801 return true;
2802 }
2803
2804 // Merge the rest of the instructions & replace the register
2805 Register Fin = MIRBuilder.buildMergeLikeInstr(DstTy, RegsToMerge).getReg(0);
2806 MRI.replaceRegWith(Dst, Fin);
2807 MI.eraseFromParent();
2808 return true;
2809}
2810
2811bool AArch64LegalizerInfo::legalizeGetRounding(MachineInstr &MI,
2812 MachineIRBuilder &MIRBuilder,
2814 LegalizerHelper &Helper) const {
2815 const LLT I32 = LLT::integer(32);
2816 const LLT I64 = LLT::integer(64);
2817
2818 Register Dst = MI.getOperand(0).getReg();
2819 Register FPCR64 = MRI.createGenericVirtualRegister(I64);
2820 MachineInstrBuilder GetFPCR =
2821 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, ArrayRef{FPCR64});
2822
2823 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2824 // 3->0, so we add one to the FPCR bits for the rounding mode.
2825 // Instead of shifting and then adding as `((FPCR >> 22) + 1) & 0b11` which
2826 // generates 3 instructions, we increment the rounding mode with
2827 // `(FPCR + (1 << 22))` and extract the bits. The shift and addition is done
2828 // in one instruction as `add .., .., #1024, lsl #12`, so overall we generate
2829 // one less instruction.
2830 auto FPCR32 = MIRBuilder.buildTrunc(I32, GetFPCR);
2831 auto One = MIRBuilder.buildConstant(I32, 1U << 22);
2832 auto Added = MIRBuilder.buildAdd(I32, FPCR32, One);
2833 auto LSB = MIRBuilder.buildConstant(I32, 22);
2834 auto Width = MIRBuilder.buildConstant(I32, 2);
2835 MIRBuilder.buildInstr(TargetOpcode::G_UBFX, {Dst}, {Added, LSB, Width});
2836
2837 MI.eraseFromParent();
2838 return true;
2839}
2840
2841bool AArch64LegalizerInfo::legalizeSetRounding(MachineInstr &MI,
2842 MachineIRBuilder &MIRBuilder,
2844 LegalizerHelper &Helper) const {
2845 const LLT I32 = LLT::integer(32);
2846 const LLT I64 = LLT::integer(64);
2847
2848 // AArch64 rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3,
2849 // 3->0, so calculate the new value of FPCR[23:22] as `((arg - 1) & 3) << 22`.
2850 Register RM = MI.getOperand(0).getReg();
2851 auto One = MIRBuilder.buildConstant(I32, 1);
2852 auto Subtracted = MIRBuilder.buildSub(I32, RM, One);
2853 auto Mask = MIRBuilder.buildConstant(I32, 0b11);
2854 auto Masked = MIRBuilder.buildAnd(I32, Subtracted, Mask);
2855 auto ShiftAmount = MIRBuilder.buildConstant(I32, 22);
2856 auto Shifted = MIRBuilder.buildShl(I32, Masked, ShiftAmount);
2857
2858 // Get current value of FPCR.
2859 MachineInstrBuilder GetFPCR =
2860 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, {I64});
2861
2862 // (FPCR & ~Mask) | Shifted
2863 auto FPCRMask = MIRBuilder.buildConstant(I64, ~((int64_t)0b11 << 22));
2864 auto FPCRMasked = MIRBuilder.buildAnd(I64, GetFPCR, FPCRMask);
2865 auto ShiftedS64 = MIRBuilder.buildZExt(I64, Shifted);
2866 auto FPCRUpdated = MIRBuilder.buildOr(I64, FPCRMasked, ShiftedS64);
2867
2868 // Write new FPCR.
2869 MIRBuilder.buildIntrinsic(Intrinsic::aarch64_set_fpcr, ArrayRef<Register>())
2870 .addUse(FPCRUpdated.getReg(0));
2871
2872 MI.eraseFromParent();
2873
2874 return true;
2875}
static void matchLDPSTPAddrMode(Register Root, Register &Base, int &Offset, MachineRegisterInfo &MRI)
This file declares the targeting of the Machinelegalizer class for AArch64.
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
static Error unsupported(const char *Str, const Triple &T)
Definition MachO.cpp: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:1050
LLVM_ABI APInt urem(const APInt &RHS) const
Unsigned remainder operation.
Definition APInt.cpp:1687
int64_t getSExtValue() const
Get sign extended value.
Definition APInt.h:1583
LLVM_ABI StringRef getValueAsString() const
Return the attribute's value as a string.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ ICMP_NE
not equal
Definition InstrTypes.h:762
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
Definition Function.cpp:762
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition Function.cpp:727
Abstract class that contains various methods for clients to notify about changes.
virtual void changingInstr(MachineInstr &MI)=0
This instruction is about to be mutated in some way.
virtual void changedInstr(MachineInstr &MI)=0
This instruction was mutated in some way.
static constexpr LLT float64()
Get a 64-bit IEEE double value.
LLT changeElementCount(ElementCount EC) const
Return a vector or scalar with the same element type and the new element count.
constexpr bool isScalableVector() const
Returns true if the LLT is a scalable vector.
constexpr unsigned getScalarSizeInBits() const
constexpr bool isScalar() const
static constexpr LLT scalable_vector(unsigned MinNumElements, unsigned ScalarSizeInBits)
Get a low-level scalable vector of some number of elements and element width.
static constexpr LLT vector(ElementCount EC, unsigned ScalarSizeInBits)
Get a low-level vector of some number of elements and element width.
LLT getScalarType() const
constexpr bool isPointerVector() const
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
constexpr uint16_t getNumElements() const
Returns the number of elements in a vector LLT.
static constexpr LLT float128()
Get a 128-bit IEEE quad value.
constexpr bool isVector() const
static constexpr LLT pointer(unsigned AddressSpace, unsigned SizeInBits)
Get a low-level pointer in the given address space.
constexpr TypeSize getSizeInBits() const
Returns the total size of the type. Must only be called on sized types.
constexpr ElementCount getElementCount() const
LLT divide(int Factor) const
Return a type that is Factor times smaller.
static constexpr LLT float16()
Get a 16-bit IEEE half value.
constexpr unsigned getAddressSpace() const
static constexpr LLT fixed_vector(unsigned NumElements, unsigned ScalarSizeInBits)
Get a low-level fixed-width vector of some number of elements and element width.
constexpr bool isFixedVector() const
Returns true if the LLT is a fixed vector.
static LLT integer(unsigned SizeInBits)
static constexpr LLT bfloat16()
LLT getElementType() const
Returns the vector's element type. Only valid for vector types.
static constexpr LLT float32()
Get a 32-bit IEEE float value.
bool isFloat64() const
LLT changeElementSize(unsigned NewEltSize) const
If this type is a vector, return a vector with the same number of elements but the new element size.
LegalizeRuleSet & minScalar(unsigned TypeIdx, const LLT Ty)
Ensure the scalar is at least as wide as Ty.
LegalizeRuleSet & widenScalarOrEltToNextPow2OrMinSize(unsigned TypeIdx, unsigned MinSize=0)
Widen the scalar or vector element type to the next power of two that is at least MinSize.
LegalizeRuleSet & legalFor(std::initializer_list< LLT > Types)
The instruction is legal when type index 0 is any type in the given list.
LegalizeRuleSet & maxScalarEltSameAsIf(LegalityPredicate Predicate, unsigned TypeIdx, unsigned SmallTypeIdx)
Conditionally narrow the scalar or elt to match the size of another.
LegalizeRuleSet & unsupported()
The instruction is unsupported.
LegalizeRuleSet & scalarSameSizeAs(unsigned TypeIdx, unsigned SameSizeIdx)
Change the type TypeIdx to have the same scalar size as type SameSizeIdx.
LegalizeRuleSet & bitcastIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
The specified type index is coerced if predicate is true.
LegalizeRuleSet & libcallFor(std::initializer_list< LLT > Types)
LegalizeRuleSet & minScalarOrElt(unsigned TypeIdx, const LLT Ty)
Ensure the scalar or element is at least as wide as Ty.
LegalizeRuleSet & clampMaxNumElements(unsigned TypeIdx, const LLT EltTy, unsigned MaxElements)
Limit the number of elements in EltTy vectors to at most MaxElements.
LegalizeRuleSet & clampMinNumElements(unsigned TypeIdx, const LLT EltTy, unsigned MinElements)
Limit the number of elements in EltTy vectors to at least MinElements.
LegalizeRuleSet & widenVectorEltsToVectorMinSize(unsigned TypeIdx, unsigned VectorSize)
Ensure the vector size is at least as wide as VectorSize by promoting the element.
LegalizeRuleSet & lowerIfMemSizeNotPow2()
Lower a memory operation if the memory size, rounded to bytes, is not a power of 2.
LegalizeRuleSet & minScalarEltSameAsIf(LegalityPredicate Predicate, unsigned TypeIdx, unsigned LargeTypeIdx)
Conditionally widen the scalar or elt to match the size of another.
LegalizeRuleSet & customForCartesianProduct(std::initializer_list< LLT > Types)
LegalizeRuleSet & lowerIfMemSizeNotByteSizePow2()
Lower a memory operation if the memory access size is not a round power of 2 byte size.
LegalizeRuleSet & moreElementsToNextPow2(unsigned TypeIdx)
Add more elements to the vector to reach the next power of two.
LegalizeRuleSet & narrowScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
Narrow the scalar to the one selected by the mutation if the predicate is true.
LegalizeRuleSet & lower()
The instruction is lowered.
LegalizeRuleSet & moreElementsIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
Add more elements to reach the type selected by the mutation if the predicate is true.
LegalizeRuleSet & lowerFor(std::initializer_list< LLT > Types)
The instruction is lowered when type index 0 is any type in the given list.
LegalizeRuleSet & scalarizeIf(LegalityPredicate Predicate, unsigned TypeIdx)
LegalizeRuleSet & lowerIf(LegalityPredicate Predicate)
The instruction is lowered if predicate is true.
LegalizeRuleSet & clampScalar(unsigned TypeIdx, const LLT MinTy, const LLT MaxTy)
Limit the range of scalar sizes to MinTy and MaxTy.
LegalizeRuleSet & custom()
Unconditionally custom lower.
LegalizeRuleSet & minScalarSameAs(unsigned TypeIdx, unsigned LargeTypeIdx)
Widen the scalar to match the size of another.
LegalizeRuleSet & unsupportedIf(LegalityPredicate Predicate)
LegalizeRuleSet & minScalarOrEltIf(LegalityPredicate Predicate, unsigned TypeIdx, const LLT Ty)
Ensure the scalar or element is at least as wide as Ty.
LegalizeRuleSet & widenScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation)
Widen the scalar to the one selected by the mutation if the predicate is true.
LegalizeRuleSet & alwaysLegal()
LegalizeRuleSet & clampNumElements(unsigned TypeIdx, const LLT MinTy, const LLT MaxTy)
Limit the number of elements for the given vectors to at least MinTy's number of elements and at most...
LegalizeRuleSet & maxScalarIf(LegalityPredicate Predicate, unsigned TypeIdx, const LLT Ty)
Conditionally limit the maximum size of the scalar.
LegalizeRuleSet & customIf(LegalityPredicate Predicate)
LegalizeRuleSet & widenScalarToNextPow2(unsigned TypeIdx, unsigned MinSize=0)
Widen the scalar to the next power of two that is at least MinSize.
LegalizeRuleSet & scalarize(unsigned TypeIdx)
LegalizeRuleSet & legalForCartesianProduct(std::initializer_list< LLT > Types)
The instruction is legal when type indexes 0 and 1 are both in the given list.
LegalizeRuleSet & legalForTypesWithMemDesc(std::initializer_list< LegalityPredicates::TypePairAndMemDesc > TypesAndMemDesc)
The instruction is legal when type indexes 0 and 1 along with the memory size and minimum alignment i...
LegalizeRuleSet & legalIf(LegalityPredicate Predicate)
The instruction is legal if predicate is true.
LLVM_ABI LegalizeResult lowerDynStackAlloc(MachineInstr &MI)
LLVM_ABI LegalizeResult lowerBitCount(MachineInstr &MI)
LLVM_ABI LegalizeResult lowerExtractInsertVectorElt(MachineInstr &MI)
Lower a vector extract or insert by writing the vector to a stack temporary and reloading the element...
LLVM_ABI LegalizeResult lowerAbsToCNeg(MachineInstr &MI)
const TargetLowering & getTargetLowering() const
LLVM_ABI LegalizeResult lowerFunnelShiftAsShifts(MachineInstr &MI)
LLVM_ABI MachineInstrBuilder createStackStoreLoad(const DstOp &Res, const SrcOp &Val)
Create a store of Val to a stack temporary and return a load as the same type as Res.
@ Legalized
Instruction has been legalized and the MachineFunction changed.
@ UnableToLegalize
Some kind of error has occurred and we could not legalize this instruction.
GISelChangeObserver & Observer
To keep track of changes made by the LegalizerHelper.
LLVM_ABI Register getDynStackAllocTargetPtr(Register SPReg, Register AllocSize, Align Alignment, LLT PtrTy)
MachineIRBuilder & MIRBuilder
Expose MIRBuilder so clients can set their own RecordInsertInstruction functions.
LegalizeRuleSet & getActionDefinitionsBuilder(unsigned Opcode)
Get the action definition builder for the given opcode.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
MachineMemOperand * getMachineMemOperand(MachinePointerInfo PtrInfo, MachineMemOperand::Flags F, LLT MemTy, Align BaseAlignment, const MMOMetadata &Metadata=MMOMetadata(), SyncScope::ID SSID=SyncScope::System, AtomicOrdering Ordering=AtomicOrdering::NotAtomic, AtomicOrdering FailureOrdering=AtomicOrdering::NotAtomic)
getMachineMemOperand - Allocate a new MachineMemOperand.
Helper class to build MachineInstr.
void setInsertPt(MachineBasicBlock &MBB, MachineBasicBlock::iterator II)
Set the insertion point before the specified position.
MachineInstrBuilder buildAdd(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_ADD Op0, Op1.
MachineInstrBuilder buildNot(const DstOp &Dst, const SrcOp &Src0)
Build and insert a bitwise not, NegOne = G_CONSTANT -1 Res = G_OR Op0, NegOne.
MachineInstrBuilder buildUnmerge(ArrayRef< LLT > Res, const SrcOp &Op)
Build and insert Res0, ... = G_UNMERGE_VALUES Op.
MachineInstrBuilder buildExtract(const DstOp &Res, const SrcOp &Src, uint64_t Index)
Build and insert Res0, ... = G_EXTRACT Src, Idx0.
MachineInstrBuilder buildAnd(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1)
Build and insert Res = G_AND Op0, Op1.
MachineInstrBuilder buildICmp(CmpInst::Predicate Pred, const DstOp &Res, const SrcOp &Op0, const SrcOp &Op1, std::optional< unsigned > Flags=std::nullopt)
Build and insert a Res = G_ICMP Pred, Op0, Op1.
MachineBasicBlock::iterator getInsertPt()
Current insertion point for new instructions.
MachineInstrBuilder buildZExt(const DstOp &Res, const SrcOp &Op, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_ZEXT Op.
MachineInstrBuilder buildConcatVectors(const DstOp &Res, ArrayRef< Register > Ops)
Build and insert Res = G_CONCAT_VECTORS Op0, ...
MachineInstrBuilder buildSub(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_SUB Op0, Op1.
MachineInstrBuilder buildIntrinsic(Intrinsic::ID ID, ArrayRef< Register > Res, bool HasSideEffects, bool isConvergent)
Build and insert a G_INTRINSIC instruction.
MachineInstrBuilder buildCTLZ(const DstOp &Dst, const SrcOp &Src0)
Build and insert Res = G_CTLZ Op0, Src0.
MachineInstrBuilder buildMergeLikeInstr(const DstOp &Res, ArrayRef< Register > Ops)
Build and insert Res = G_MERGE_VALUES Op0, ... or Res = G_BUILD_VECTOR Op0, ... or Res = G_CONCAT_VEC...
MachineInstrBuilder buildLoad(const DstOp &Res, const SrcOp &Addr, MachineMemOperand &MMO)
Build and insert Res = G_LOAD Addr, MMO.
MachineInstrBuilder buildPtrAdd(const DstOp &Res, const SrcOp &Op0, const SrcOp &Op1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_PTR_ADD Op0, Op1.
MachineInstrBuilder buildBitReverse(const DstOp &Dst, const SrcOp &Src)
Build and insert Dst = G_BITREVERSE Src.
MachineInstrBuilder buildShl(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
MachineInstrBuilder buildStore(const SrcOp &Val, const SrcOp &Addr, MachineMemOperand &MMO)
Build and insert G_STORE Val, Addr, MMO.
MachineInstrBuilder buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
MachineInstrBuilder buildCTPOP(const DstOp &Dst, const SrcOp &Src0)
Build and insert Res = G_CTPOP Op0, Src0.
MachineFunction & getMF()
Getter for the function we currently build.
MachineInstrBuilder buildExtOrTrunc(unsigned ExtOpc, const DstOp &Res, const SrcOp &Op)
Build and insert Res = ExtOpc, Res = G_TRUNC Op, or Res = COPY Op depending on the differing sizes of...
MachineInstrBuilder buildTrunc(const DstOp &Res, const SrcOp &Op, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_TRUNC Op.
const MachineBasicBlock & getMBB() const
Getter for the basic block we currently build.
MachineInstrBuilder buildAnyExt(const DstOp &Res, const SrcOp &Op)
Build and insert Res = G_ANYEXT Op0.
MachineInstrBuilder buildBitcast(const DstOp &Dst, const SrcOp &Src)
Build and insert Dst = G_BITCAST Src.
MachineRegisterInfo * getMRI()
Getter for MRI.
MachineInstrBuilder buildFPTrunc(const DstOp &Res, const SrcOp &Op, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_FPTRUNC Op.
MachineInstrBuilder buildOr(const DstOp &Dst, const SrcOp &Src0, const SrcOp &Src1, std::optional< unsigned > Flags=std::nullopt)
Build and insert Res = G_OR Op0, Op1.
MachineInstrBuilder buildCopy(const DstOp &Res, const SrcOp &Op)
Build and insert Res = COPY Op.
MachineInstrBuilder buildMaskLowPtrBits(const DstOp &Res, const SrcOp &Op0, uint32_t NumBits)
Build and insert Res = G_PTRMASK Op0, G_CONSTANT (1 << NumBits) - 1.
virtual MachineInstrBuilder buildConstant(const DstOp &Res, const ConstantInt &Val)
Build and insert Res = G_CONSTANT Val.
Register getReg(unsigned Idx) const
Get the register for the operand index.
const MachineInstrBuilder & addUse(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register use operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & add(const MachineOperand &MO) const
const MachineInstrBuilder & cloneMemRefs(const MachineInstr &OtherMI) const
Representation of each machine instruction.
const MachineOperand & getOperand(unsigned i) const
@ MOLoad
The memory access reads data.
@ MOStore
The memory access writes data.
LLVM_ABI void setReg(Register Reg)
Change the register this operand corresponds to.
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
LLT getType(Register Reg) const
Get the low-level type of Reg or LLT{} if Reg is not a generic (target independent) virtual register.
LLVM_ABI void setRegClass(Register Reg, const TargetRegisterClass *RC)
setRegClass - Set the register class of the specified virtual register.
LLVM_ABI Register createGenericVirtualRegister(LLT Ty, StringRef Name="")
Create and return a new generic virtual register with low-level type Ty.
const TargetRegisterInfo * getTargetRegisterInfo() const
LLVM_ABI void replaceRegWith(Register FromReg, Register ToReg)
replaceRegWith - Replace all instances of FromReg with ToReg in the machine function.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Register getStackPointerRegisterToSaveRestore() const
If a physical register, this specifies the register that llvm.savestack/llvm.restorestack should save...
Primary interface to the complete machine description for the target machine.
Target - Wrapper for Target specific information.
LLVM Value Representation.
Definition Value.h:75
constexpr LeafTy divideCoefficientBy(ScalarTy RHS) const
We do not provide the '/' operator here because division for polynomial types does not work in the sa...
Definition TypeSize.h:252
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ MO_NC
MO_NC - Indicates whether the linker is expected to check the symbol reference for overflow.
@ MO_PAGEOFF
MO_PAGEOFF - A symbol operand with this flag represents the offset of that symbol within a 4K page.
@ MO_GOT
MO_GOT - This flag indicates that a symbol operand represents the address of the GOT entry for the sy...
@ MO_PREL
MO_PREL - Indicates that the bits of the symbol operand represented by MO_G0 etc are PC relative.
@ MO_PAGE
MO_PAGE - A symbol operand with this flag represents the pc-relative offset of the 4K page containing...
@ MO_TAGGED
MO_TAGGED - With MO_PAGE, indicates that the page includes a memory tag in bits 56-63.
@ MO_G3
MO_G3 - A symbol operand with this flag (granule 3) represents the high 16-bits of a 64-bit address,...
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
LLVM_ABI LegalityPredicate scalarOrEltWiderThan(unsigned TypeIdx, unsigned Size)
True iff the specified type index is a scalar or a vector with an element type that's wider than the ...
LLVM_ABI LegalityPredicate isPointerVector(unsigned TypeIdx)
True iff the specified type index is a vector of pointers (with any address space).
LLVM_ABI LegalityPredicate typeInSet(unsigned TypeIdx, std::initializer_list< LLT > TypesInit)
True iff the given type index is one of the specified types.
LLVM_ABI LegalityPredicate smallerThan(unsigned TypeIdx0, unsigned TypeIdx1)
True iff the first type index has a smaller total bit size than second type index.
LLVM_ABI LegalityPredicate atomicOrderingAtLeastOrStrongerThan(unsigned MMOIdx, AtomicOrdering Ordering)
True iff the specified MMO index has at an atomic ordering of at Ordering or stronger.
Predicate any(Predicate P0, Predicate P1)
True iff P0 or P1 are true.
LLVM_ABI LegalityPredicate isVector(unsigned TypeIdx)
True iff the specified type index is a vector.
Predicate all(Predicate P0, Predicate P1)
True iff P0 and P1 are true.
LLVM_ABI LegalityPredicate typeIs(unsigned TypeIdx, LLT TypesInit)
True iff the given type index is the specified type.
LLVM_ABI LegalityPredicate scalarWiderThan(unsigned TypeIdx, unsigned Size)
True iff the specified type index is a scalar that's wider than the given size.
LLVM_ABI LegalityPredicate scalarNarrowerThan(unsigned TypeIdx, unsigned Size)
True iff the specified type index is a scalar that's narrower than the given size.
@ Bitcast
Perform the operation on a different, but equivalently sized type.
LLVM_ABI LegalizeMutation moreElementsToNextPow2(unsigned TypeIdx, unsigned Min=0)
Add more elements to the type for the given type index to the next power of.
LLVM_ABI LegalizeMutation scalarize(unsigned TypeIdx)
Break up the vector type for the given type index into the element type.
LLVM_ABI LegalizeMutation changeElementTo(unsigned TypeIdx, unsigned FromTypeIdx)
Keep the same scalar or element type as the given type index.
LLVM_ABI LegalizeMutation widenScalarOrEltToNextPow2(unsigned TypeIdx, unsigned Min=0)
Widen the scalar type or vector element type for the given type index to the next power of 2.
LLVM_ABI LegalizeMutation changeTo(unsigned TypeIdx, LLT Ty)
Select this specific type for the given type index.
LLVM_ABI LegalizeMutation changeElementSizeTo(unsigned TypeIdx, unsigned FromTypeIdx)
Change the scalar size or element size to have the same scalar size as type index FromIndex.
operand_type_match m_Reg()
ConstantMatch< APInt > m_ICst(APInt &Cst)
bool mi_match(Reg R, const MachineRegisterInfo &MRI, Pattern &&P)
BinaryOp_match< LHS, RHS, TargetOpcode::G_PTR_ADD, false > m_GPtrAdd(const LHS &L, const RHS &R)
Invariant opcodes: All instruction sets have these as their low opcodes.
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::optional< APInt > isConstantOrConstantSplatVector(Register Def, const MachineRegisterInfo &MRI)
Determines if Def defines a constant integer or a splat vector of constant integers.
Definition Utils.cpp:1517
@ Offset
Definition DWP.cpp:578
LLVM_ABI void constrainSelectedInstRegOperands(MachineInstr &I, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI)
Mutate the newly-selected instruction I to constrain its (possibly generic) virtual register operands...
Definition Utils.cpp:159
constexpr bool isPowerOf2_64(uint64_t Value)
Return true if the argument is a power of two > 0 (64 bit edition.)
Definition MathExtras.h:285
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
uint64_t PowerOf2Ceil(uint64_t A)
Returns the power of two which is greater than or equal to the given value.
Definition MathExtras.h:386
std::function< bool(const LegalityQuery &)> LegalityPredicate
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:144
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
AtomicOrdering
Atomic ordering for LLVM's memory model.
@ Add
Sum of integers.
IntPtrTy
Definition InstrProf.h:82
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
constexpr bool isShiftedInt(int64_t x)
Checks if a signed integer is an N bit number shifted left by S.
Definition MathExtras.h:183
constexpr unsigned BitWidth
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
LLVM_ABI std::optional< ValueAndVReg > getIConstantVRegValWithLookThrough(Register VReg, const MachineRegisterInfo &MRI, bool LookThroughInstrs=true)
If VReg is defined by a statically evaluable chain of instructions rooted on a G_CONSTANT returns its...
Definition Utils.cpp:436
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1947
Align assumeAligned(uint64_t Value)
Treats the value 0 as a 1, so Align is always at least 1.
Definition Alignment.h:100
unsigned Log2(Align A)
Returns the log2 of the alignment.
Definition Alignment.h:197
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
The LegalityQuery object bundles together all the information that's needed to decide whether a given...
ArrayRef< MemDesc > MMODescrs
Operations which require memory can use this to place requirements on the memory type for each MMO.
ArrayRef< LLT > Types
This class contains a discriminated union of information about pointers in memory operands,...