aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/include/clang/Basic/arm_mve_defs.td
blob: 1a090c08cc8531de8a4f1d0f0e4fd22669f4b48c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
//===- arm_mve_defs.td - definitions and infrastructure for arm_mve.td ----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// The definitions in this file are designed to work in close conjunction with
// clang/utils/TableGen/MveEmitter.cpp. Comments in there will probably be
// useful as well.
//
//===----------------------------------------------------------------------===//

// -----------------------------------------------------------------------------
// Forward declarations.
class Type;

// -----------------------------------------------------------------------------
// Dummy record used as the dag operator for the argument list of an intrinsic.
//
// We store arguments as a dag rather than a list<Type> so that we can give
// each one a name, to be used in codegen. For example, (args Vector:$a,
// Scalar:$b) defines the names $a and $b which the specification of the code
// for that intrinsic can refer to.

def args;

// -----------------------------------------------------------------------------
// Family of nodes for use in the codegen dag for an intrinsic, corresponding
// to function calls that return LLVM IR nodes.
class IRBuilderParam<int index_> { int index = index_; }
class IRBuilderAddrParam<int index_> : IRBuilderParam<index_>;
class IRBuilderIntParam<int index_, string type_> : IRBuilderParam<index_> {
  string type = type_;
}
class IRBuilderBase {
  // The prefix of the function call, including an open parenthesis.
  string prefix;

  // Any parameters that have types that have to be treated specially by the
  // Tablegen back end. Generally these will be types other than llvm::Value *,
  // although not all other types need special treatment (e.g. llvm::Type *).
  list<IRBuilderParam> special_params = [];
}
class IRBuilder<string func> : IRBuilderBase {
  // The usual case: a method called on the code gen function's instance of
  // llvm::IRBuilder.
  let prefix = "Builder." # func # "(";
}
class IRFunction<string func> : IRBuilderBase {
  // Some other function that doesn't use the IRBuilder at all.
  let prefix = func # "(";
}
class CGHelperFn<string func> : IRBuilderBase {
  // A helper function defined in CGBuiltin.cpp, which takes the IRBuilder as
  // an argument.
  let prefix = func # "(Builder, ";
}
class CGFHelperFn<string func> : IRBuilderBase {
  // Like CGHelperFn, but also takes the CodeGenFunction itself.
  let prefix = func # "(Builder, this, ";
}
def add: IRBuilder<"CreateAdd">;
def mul: IRBuilder<"CreateMul">;
def not: IRBuilder<"CreateNot">;
def or: IRBuilder<"CreateOr">;
def and: IRBuilder<"CreateAnd">;
def xor: IRBuilder<"CreateXor">;
def sub: IRBuilder<"CreateSub">;
def shl: IRBuilder<"CreateShl">;
def lshr: IRBuilder<"CreateLShr">;
def immshr: CGHelperFn<"MVEImmediateShr"> {
  let special_params = [IRBuilderIntParam<1, "unsigned">,
                        IRBuilderIntParam<2, "bool">];
}
def fadd: IRBuilder<"CreateFAdd">;
def fmul: IRBuilder<"CreateFMul">;
def fsub: IRBuilder<"CreateFSub">;
def load: IRBuilder<"CreateLoad"> {
  let special_params = [IRBuilderAddrParam<0>];
}
def store: IRBuilder<"CreateStore"> {
  let special_params = [IRBuilderAddrParam<1>];
}
def xval: IRBuilder<"CreateExtractValue"> {
  let special_params = [IRBuilderIntParam<1, "unsigned">];
}
def ielt_const: IRBuilder<"CreateInsertElement"> {
  let special_params = [IRBuilderIntParam<2, "uint64_t">];
}
def ielt_var: IRBuilder<"CreateInsertElement">;
def xelt_var: IRBuilder<"CreateExtractElement">;
def trunc: IRBuilder<"CreateTrunc">;
def bitcast: IRBuilder<"CreateBitCast">;
def vreinterpret: CGFHelperFn<"ARMMVEVectorReinterpret">;
def extend: CGHelperFn<"SignOrZeroExtend"> {
  let special_params = [IRBuilderIntParam<2, "bool">];
}
def zeroinit: IRFunction<"llvm::Constant::getNullValue">;
def int_min: CGHelperFn<"ARMMVEConstantSplat<1,0>">;
def int_max: CGHelperFn<"ARMMVEConstantSplat<0,1>">;
def uint_max: CGHelperFn<"ARMMVEConstantSplat<1,1>">;
def undef: IRFunction<"UndefValue::get">;
def icmp_eq: IRBuilder<"CreateICmpEQ">;
def icmp_ne: IRBuilder<"CreateICmpNE">;
def icmp_ugt: IRBuilder<"CreateICmpUGT">;
def icmp_uge: IRBuilder<"CreateICmpUGE">;
def icmp_ult: IRBuilder<"CreateICmpULT">;
def icmp_ule: IRBuilder<"CreateICmpULE">;
def icmp_sgt: IRBuilder<"CreateICmpSGT">;
def icmp_sge: IRBuilder<"CreateICmpSGE">;
def icmp_slt: IRBuilder<"CreateICmpSLT">;
def icmp_sle: IRBuilder<"CreateICmpSLE">;
def fcmp_eq: IRBuilder<"CreateFCmpOEQ">;
def fcmp_ne: IRBuilder<"CreateFCmpUNE">; // not O: it must return true on NaNs
def fcmp_gt: IRBuilder<"CreateFCmpOGT">;
def fcmp_ge: IRBuilder<"CreateFCmpOGE">;
def fcmp_lt: IRBuilder<"CreateFCmpOLT">;
def fcmp_le: IRBuilder<"CreateFCmpOLE">;
def splat: CGHelperFn<"ARMMVEVectorSplat">;
def select: IRBuilder<"CreateSelect">;
def fneg: IRBuilder<"CreateFNeg">;
def sitofp: IRBuilder<"CreateSIToFP">;
def uitofp: IRBuilder<"CreateUIToFP">;
def fptosi: IRBuilder<"CreateFPToSI">;
def fptoui: IRBuilder<"CreateFPToUI">;
def vrev: CGHelperFn<"ARMMVEVectorElementReverse"> {
  let special_params = [IRBuilderIntParam<1, "unsigned">];
}
def unzip: CGHelperFn<"VectorUnzip"> {
  let special_params = [IRBuilderIntParam<1, "bool">];
}
def zip: CGHelperFn<"VectorZip">;

// Trivial 'codegen' function that just returns its argument. Useful
// for wrapping up a variable name like $foo into a thing you can pass
// around as type 'dag'.
def id: IRBuilderBase {
  // All the other cases of IRBuilderBase use 'prefix' to specify a function
  // call, including the open parenthesis. MveEmitter puts the closing paren on
  // the end. So if we _just_ specify an open paren with no function name
  // before it, then the generated C++ code will simply wrap the input value in
  // parentheses, returning it unchanged.
  let prefix = "(";
}

// Helper for making boolean flags in IR
def i1: IRBuilderBase {
  let prefix = "llvm::ConstantInt::get(Builder.getInt1Ty(), ";
  let special_params = [IRBuilderIntParam<0, "bool">];
}

// A node that makes an Address out of a pointer-typed Value, by
// providing an alignment as the second argument.
def address;

// Another node class you can use in the codegen dag. This one corresponds to
// an IR intrinsic function, which has to be specialized to a particular list
// of types.
class IRIntBase<string name_, list<Type> params_ = [], bit appendKind_ = 0> {
  string intname = name_;       // base name of the intrinsic
  list<Type> params = params_;  // list of parameter types

  // If this flag is set, then the IR intrinsic name will get a suffix _s, _u
  // or _f depending on whether the main parameter type of the ACLE intrinsic
  // being generated is a signed integer, unsigned integer, or float. Mostly
  // this is useful for signed vs unsigned integers, because the ACLE
  // intrinsics and the source-level integer types distinguish them, but at IR
  // level the distinction has moved from the type system into the operations
  // and you just have i32 or i16 etc. So when an IR intrinsic has to vary with
  // signedness, you set this bit, and then you can still put the signed and
  // unsigned versions in the same subclass of Intrinsic, and the Tablegen
  // backend will take care of adding _s or _u as appropriate in each instance.
  bit appendKind = appendKind_;
}

// Mostly we'll be using @llvm.arm.mve.* intrinsics, so here's a trivial
// subclass that puts on that prefix.
class IRInt<string name, list<Type> params = [], bit appendKind = 0>
      : IRIntBase<"arm_mve_" # name, params, appendKind>;

// The 'seq' node in a codegen dag specifies a set of IR operations to be
// performed in order. It has the special ability to define extra variable
// names, on top of the ones that refer to the intrinsic's parameters. For
// example:
//
//   (seq (foo this, that):$a,
//        (bar this, $a):$b
//        (add $a, $b))
//
// defines the name $a to refer to the return value of the 'foo' operation;
// then the 'bar' operation uses $a as one of its arguments, and the return
// value of that is assigned the name $b; finally, $a and $b are added to give
// the return value of the seq construction as a whole.
def seq;

// Another magic operation is 'unsignedflag', which you give a scalar
// _type_ as an argument, and it expands into 1 for an unsigned type
// and 0 for a signed (or floating) one.
def unsignedflag;

// 'bitsize' also takes a scalar type, and expands into an integer
// constant giving its size in bits.
def bitsize;

// If you put CustomCodegen<"foo"> in an intrinsic's codegen field, it
// indicates that the IR generation for that intrinsic is done by handwritten
// C++ and not autogenerated at all. The effect in the MVE builtin codegen
// function is to break out of the main switch and fall through to the
// manual-codegen cases below it, having set the CustomCodeGenType enumerated
// variable to the value given by the 'type' string here.
class CustomCodegen<string type_> { string type = type_; }

// -----------------------------------------------------------------------------
// System for building up complex instances of Type from simple ones.

// ComplexType is used to represent any more complicated type: vectors,
// multivectors, pointers etc. Its dag argument specifies how the type should
// be constructed from simpler types. The operator of the dag will always be an
// instance of ComplexTypeOp, defined below.
class ComplexType<dag spec_>: Type { dag spec = spec_; }

// Operators you can use in the ComplexType spec dag. These are an intermediate
// layer, interpreted by MveEmitter::getType() in the Tablegen backend, and
// only used in the definitions below. Actual intrinsic definitions in
// arm_mve.td will use the defs defined below here.
class ComplexTypeOp;
def CTO_Parameter: ComplexTypeOp;
def CTO_Vec: ComplexTypeOp;
def CTO_Pred: ComplexTypeOp;
class CTO_Tuple<int n_>: ComplexTypeOp { int n = n_; }
class CTO_Pointer<bit const_>: ComplexTypeOp { bit const = const_; }
def CTO_CopyKind: ComplexTypeOp;
class CTO_ScaleSize<int num_, int denom_>: ComplexTypeOp {
  int num = num_;
  int denom = denom_;
}

// -----------------------------------------------------------------------------
// Instances of Type intended to be used directly in the specification of an
// intrinsic in arm_mve.td.

// The type Void can be used for the return type of an intrinsic, and as the
// parameter type for intrinsics that aren't actually parameterised by any kind
// of _s32 / _f16 / _u8 suffix.
def Void : Type;

// A wrapper you can put on an intrinsic's argument type to prevent it from
// being automatically promoted to i32 from a smaller integer type.
class unpromoted<Type t> : Type { Type underlying_type = t; }

// Primitive types: base class, and an instance for the set of scalar integer
// and floating types that MVE uses.
class PrimitiveType<string kind_, int size_>: Type {
  string kind = kind_;
  int size = size_;
  string nameOverride = "";
}

// The type records defined by these foreaches have names like s32, f16, u8.
foreach size = [8, 16, 32, 64] in
  foreach kind = ["u", "s"] in
    def kind # size: PrimitiveType<kind, size>;
foreach size = [16, 32] in
  foreach kind = ["f"] in
    def kind # size: PrimitiveType<kind, size>;

// Sometimes we need to refer to a type by a different name in C, when
// ACLE defines a function parameter to be something like 'unsigned'
// rather than uint32_t.
def uint: PrimitiveType<"u", 32> { let nameOverride = "unsigned"; }
def sint: PrimitiveType<"s", 32> { let nameOverride = "int"; }

// VecOf<t> expects t to be a scalar, and gives a 128-bit vector of whatever it
// is.
class VecOf<Type t>: ComplexType<(CTO_Vec t)>;

// NarrowedVecOf<t,v> expects t to be a scalar type, and v to be a vector
// type. It returns a vector type whose element type is t, and whose lane
// count is the same as the lane count of v. (Used as an intermediate value
// type in the IR representation of a widening load: you load a vector of
// small things out of memory, and then zext/sext them into a full 128-bit
// output vector.)
class NarrowedVecOf<Type t, Type v>: ComplexType<(CTO_Vec t, v)>;

// PredOf expects t to be a scalar, and expands to a predicate vector which
// (logically speaking) has the same number of lanes as VecOf<t> would.
class PredOf<Type t>: ComplexType<(CTO_Pred t)>;

// Scalar expands to whatever is the main parameter type of the current
// intrinsic. Vector and Predicate expand to the vector and predicate types
// corresponding to that.
def Scalar: ComplexType<(CTO_Parameter)>;
def Vector: VecOf<Scalar>;
def Predicate: PredOf<Scalar>;

// MultiVector<n> expands to a type containing n instances of Vector. (There's
// no need to define this for a general underlying vector type, since it's only
// used by vld2q and friends, which don't need that generality.)
class MultiVector<int n>: ComplexType<(CTO_Tuple<n> Vector)>;

// Ptr<t> and CPtr<t> expand to a pointer to t, or a pointer to const t,
// respectively.
class Ptr<Type t>: ComplexType<(CTO_Pointer<0> t)>;
class CPtr<Type t>: ComplexType<(CTO_Pointer<1> t)>;

// CopyKind<s,k> expects s and k to be scalar types. It returns a scalar type
// whose kind (signed, unsigned or float) matches that of k, and whose size
// matches that of s.
class CopyKind<Type s, Type k>: ComplexType<(CTO_CopyKind s, k)>;

// DoubleSize<k> expects k to be a scalar type. It returns a scalar type
// whose kind (signed, unsigned or float) matches that of k, and whose size
// is double that of k, if possible.
class DoubleSize<Type k> : ComplexType<(CTO_ScaleSize<2, 1> k)>;
class HalfSize<Type k>   : ComplexType<(CTO_ScaleSize<1, 2> k)>;

// Unsigned<t> expects t to be a scalar type, and expands to the unsigned
// integer scalar of the same size. So it returns u16 if you give it s16 or
// f16 (or u16 itself). Similarly, Signed<t> makes the type signed.
class Unsigned<Type t>: ComplexType<(CTO_CopyKind t, u32)>;
class Signed<Type t>: ComplexType<(CTO_CopyKind t, s32)>;

// UScalar and UVector expand to the unsigned-integer versions of
// Scalar and Vector. SScalar and SVector are signed-integer versions.
def UScalar: Unsigned<Scalar>;
def UVector: VecOf<UScalar>;
def SScalar: Signed<Scalar>;
def SVector: VecOf<SScalar>;

// DblVector expands to a vector of scalars of size twice the size of Scalar.
// DblPredicate expands to a predicate corresponding to DblVector
// HalfVector, similarly, expands to a vector of half-sized scalars. And
// UHalfVector is a vector of half-sized _unsigned integers_.
def DblVector: VecOf<DoubleSize<Scalar>>;
def DblPredicate: PredOf<DoubleSize<Scalar>>;
def HalfScalar: HalfSize<Scalar>;
def HalfVector: VecOf<HalfScalar>;
def UHalfScalar: Unsigned<HalfSize<Scalar>>;
def UHalfVector: VecOf<UHalfScalar>;

// Expands to the 32-bit integer of the same signedness as Scalar.
def Scalar32: CopyKind<u32, Scalar>;
// Expands to the 64-bit integer of the same signedness as Scalar.
def Scalar64: CopyKind<u64, Scalar>;

// -----------------------------------------------------------------------------
// Internal definitions for specifying immediate arguments for an intrinsic.

class ImmediateBounds;
class Immediate<Type type_, ImmediateBounds bounds_>: Type {
  Type type = type_;
  ImmediateBounds bounds = bounds_;
  string extra;
  string extraarg;
}
class IB_ConstRange<int lo_, int hi_> : ImmediateBounds {
  int lo = lo_;
  int hi = hi_;
}
def IB_UEltValue : ImmediateBounds;
def IB_LaneIndex : ImmediateBounds;
class IB_EltBit<int base_, Type type_ = Scalar> : ImmediateBounds {
  int base = base_;
  Type type = type_;
}
def IB_ExtraArg_LaneSize;

// -----------------------------------------------------------------------------
// End-user definitions for immediate arguments.

// imm_simd and imm_simd_restrictive are used for the immediate operands to
// intrinsics like vmvnq or vorrq. imm_simd_restrictive has to be an 8-bit
// value shifted left by a whole number of bytes; imm_simd_vmvn can also be of
// the form 0xXXFF for some byte value XX.
def imm_simd_restrictive : Immediate<Scalar, IB_UEltValue> {
  let extra = "ShiftedByte";
  let extraarg = "!lanesize";
}
def imm_simd_vmvn : Immediate<Scalar, IB_UEltValue> {
  let extra = "ShiftedByteOrXXFF";
  let extraarg = "!lanesize";
}

// imm_1toN can take any value from 1 to N inclusive, where N is the number of
// bits in the main parameter type. (E.g. an immediate shift count, in an
// intrinsic that shifts every lane of a vector by the same amount.)
//
// imm_0toNm1 is the same but with the range offset by 1, i.e. 0 to N-1
// inclusive.
//
// imm_1toHalfN is like imm_1toN, but applied to a half-width type.
// (So if Scalar is s16, for example, it'll give you the range 1 to 8.)
def imm_1toN : Immediate<sint, IB_EltBit<1>>;
def imm_0toNm1 : Immediate<sint, IB_EltBit<0>>;
def imm_1toHalfN : Immediate<sint, IB_EltBit<1, HalfSize<Scalar>>>;

// imm_lane has to be the index of a vector lane in the main vector type, i.e
// it can range from 0 to (128 / size of scalar)-1 inclusive. (e.g. vgetq_lane)
def imm_lane : Immediate<sint, IB_LaneIndex>;

// imm_1to32 can be in the range 1 to 32, unconditionally. (e.g. scalar shift
// intrinsics)
def imm_1to32 : Immediate<sint, IB_ConstRange<1, 32>>;

// imm_1248 can be 1, 2, 4 or 8. (e.g. vidupq)
def imm_1248 : Immediate<sint, IB_ConstRange<1, 8>> {
  let extra = "Power2";
}

// imm_mem7bit<n> is a valid immediate offset for a load/store intrinsic whose
// memory access size is n bytes (e.g. 1 for vldrb_[whatever], 2 for vldrh,
// ...). The set of valid immediates for these is {-127*n, ..., -1*n, 0*n, 1*n,
// ..., 127*n}.
class imm_mem7bit<int membytes>
  : Immediate<sint, IB_ConstRange<!mul(membytes, -127), !mul(membytes, 127)>> {
  let extra = !if(!eq(membytes, 1), ?, "Multiple");
  let extraarg = !cast<string>(membytes);
}

// -----------------------------------------------------------------------------
// Specification of ways that the full name of an intrinsic can be mapped to
// its shorter polymorphic name.

class PolymorphicNameType<int nt_, string x_> {
  int NumTypeSuffixesToDiscard = nt_;
  string ExtraSuffixToDiscard = x_;
}

// PNT_None: the intrinsic is not polymorphic at all, so its short name is the
// same as its long name. (E.g. scalar shift intrinsics such as uqshl.)
def PNT_None:   PolymorphicNameType<0, ?>;

// PNT_Type: the usual case, in which the polymorphic name is made by dropping
// the type suffix, so it ends up the same as the Tablegen record name. E.g.
// vaddq_u16 -> vaddq.
def PNT_Type:   PolymorphicNameType<1, ?>;

// PNT_2Type: the polymorphic name is made by dropping _two_ type suffixes.
// E.g. vcvtq_f16_u16 -> vcvtq.
def PNT_2Type:  PolymorphicNameType<2, ?>;

// PNT_NType: the polymorphic name is made by dropping an "_n" suffix and a
// type. E.g. vaddq_n_u16 -> vaddq.
def PNT_NType:  PolymorphicNameType<1, "n">;

// PNT_NType: the polymorphic name is made by just dropping an "_n" suffix
// (even if it isn't at the end of the name). E.g. vidupq_n_u16 -> vidupq_u16.
def PNT_N:      PolymorphicNameType<0, "n">;

// PNT_WBType: the polymorphic name is made by dropping an "_wb" suffix and a
// type. E.g. vidupq_m_wb_u16 -> vidupq_m.
def PNT_WBType: PolymorphicNameType<1, "wb">;

// PNT_WB: the polymorphic name is made by just dropping "_wb". E.g.
// vidupq_wb_u16 -> vidupq_u16.
def PNT_WB:     PolymorphicNameType<0, "wb">;

// -----------------------------------------------------------------------------
// The main class Intrinsic. Define one of these for each family of ACLE
// intrinsics which are the same apart from some final type suffix (e.g.
// vaddq_{s8,u8,f16,...}.
//
// The record's name plus that type suffix is taken to be the full unambiguous
// name of the function. Its shorter polymorphic name is constructed from that
// in turn, in a way specified by the PolymorphicNameType system above.

class Intrinsic<Type ret_, dag args_, dag codegen_> {
  // List of parameter types to suffix to this intrinsic's name. A separate
  // actual ACLE intrinsic will be generated for each of these. Set it to
  // [Void] if the intrinsic is not polymorphic at all.
  list<Type> params;

  // Return type and arguments for the intrinsic.
  Type ret = ret_;
  dag args = args_;

  // Specification of how to generate its IR.
  dag codegen = codegen_;

  // Default to PNT_Type, which is by far the most common case.
  PolymorphicNameType pnt = PNT_Type;

  // A very few intrinsics _only_ have a polymorphic name.
  bit polymorphicOnly = 0;

  // True if the builtin has to avoid evaluating its arguments.
  bit nonEvaluating = 0;

  // True if the intrinsic needs only the C header part (no codegen, semantic
  // checks, etc). Used for redeclaring MVE intrinsics in the arm_cde.h header.
  bit headerOnly = 0;

  // Use to override the suffix letter to make e.g.vfooq_p16
  // with an override suffix letter of "p".
  string overrideKindLetter = "";

  // Name of the architecture extension, used in the Clang builtin name
  string builtinExtension = "mve";
}

// Sometimes you have to use two separate Intrinsic declarations to
// declare intrinsics that are logically the same family (e.g. vaddq,
// because it needs to expand to an Add or FAdd IR node depending on
// type). For that purpose, you can derive from NameOverride to
// specify the intrinsic's base name independently of the Tablegen
// record name.

class NameOverride<string basename_> {
  string basename = basename_;
}

// A wrapper to define both _m and _x versions of a predicated
// intrinsic.
//
// We provide optional parameters to override the polymorphic name
// types separately for the _m and _x variants, because sometimes they
// polymorph differently (typically because the type of the inactive
// parameter can be used as a disambiguator if it's present).
multiclass IntrinsicMX<Type rettype, dag arguments, dag cg,
                       bit wantXVariant = 1,
                       string nameSuffix = "",
                       PolymorphicNameType pnt_m = PNT_Type,
                       PolymorphicNameType pnt_x = PNT_Type> {
  // The _m variant takes an initial parameter called $inactive, which
  // provides the input value of the output register, i.e. all the
  // inactive lanes in the predicated operation take their values from
  // this.
  def : Intrinsic<rettype, !con((args rettype:$inactive), arguments), cg>,
        NameOverride<NAME # "_m" # nameSuffix> {
    let pnt = pnt_m;
  }

  if wantXVariant then {
    // The _x variant leaves off that parameter, and simply uses an
    // undef value of the same type.

    def : Intrinsic<rettype, arguments, (seq (undef rettype):$inactive, cg)>,
          NameOverride<NAME # "_x" # nameSuffix> {
      let pnt = pnt_x;
    }
  }
}

// Same as above, but with an additional parameter 'basename' which overrides
// the C intrinsic base name
multiclass IntrinsicMXNameOverride<Type rettype, dag arguments, dag cg,
                                   string basename, bit wantXVariant = 1,
                                   string nameSuffix = "",
                                   PolymorphicNameType pnt_m = PNT_Type,
                                   PolymorphicNameType pnt_x = PNT_Type> {
  def "_m" # nameSuffix:
     Intrinsic<rettype, !con((args rettype:$inactive), arguments), cg>,
     NameOverride<basename # "_m" # nameSuffix> {
    let pnt = pnt_m;
  }

  if wantXVariant then {
    def "_x" # nameSuffix:
      Intrinsic<rettype, arguments, (seq (undef rettype):$inactive, cg)>,
      NameOverride<basename # "_x" # nameSuffix> {
      let pnt = pnt_x;
    }
  }
}


// -----------------------------------------------------------------------------
// Convenience lists of parameter types. 'T' is just a container record, so you
// can define a typical intrinsic with 'let Params = T.Usual', or similar,
// instead of having to repeat a long list every time.

def T {
  list<Type> None = [Void];
  list<Type> Signed = [s8, s16, s32];
  list<Type> Unsigned = [u8, u16, u32];
  list<Type> Int = Signed # Unsigned;
  list<Type> Float = [f16, f32];
  list<Type> Usual = Int # Float;
  list<Type> Int8 = [s8, u8];
  list<Type> Int16 = [s16, u16];
  list<Type> Int32 = [s32, u32];
  list<Type> Int64 = [s64, u64];
  list<Type> Poly = [u8, u16]; // Actually p8 and p16
  list<Type> All8 = Int8;
  list<Type> All16 = Int16 # [f16];
  list<Type> All32 = Int32 # [f32];
  list<Type> All64 = Int64;
  list<Type> All = Usual # All64;
}

// -----------------------------------------------------------------------------
// Container record for DAG constant values. These constants are used because
// bit/int class/multiclass parameters cannot be used to produce a dag node:
// for example (u32 x) where x is 0 is transformed into (u32 { 0 }) by the
// Tablegen parser.
def V {
  dag False = (u32 0);
  dag True  = (u32 1);
}