1356843Sdim//===- arm_mve_defs.td - definitions and infrastructure for arm_mve.td ----===//
2356843Sdim//
3356843Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4356843Sdim// See https://llvm.org/LICENSE.txt for license information.
5356843Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6356843Sdim//
7356843Sdim//===----------------------------------------------------------------------===//
8356843Sdim//
9356843Sdim// The definitions in this file are designed to work in close conjunction with
10356843Sdim// clang/utils/TableGen/MveEmitter.cpp. Comments in there will probably be
11356843Sdim// useful as well.
12356843Sdim//
13356843Sdim//===----------------------------------------------------------------------===//
14356843Sdim
15356843Sdim// -----------------------------------------------------------------------------
16356843Sdim// Forward declarations.
17356843Sdimclass Type;
18356843Sdim
19356843Sdim// -----------------------------------------------------------------------------
20356843Sdim// Dummy record used as the dag operator for the argument list of an intrinsic.
21356843Sdim//
22356843Sdim// We store arguments as a dag rather than a list<Type> so that we can give
23356843Sdim// each one a name, to be used in codegen. For example, (args Vector:$a,
24356843Sdim// Scalar:$b) defines the names $a and $b which the specification of the code
25356843Sdim// for that intrinsic can refer to.
26356843Sdim
27356843Sdimdef args;
28356843Sdim
29356843Sdim// -----------------------------------------------------------------------------
30356843Sdim// Family of nodes for use in the codegen dag for an intrinsic, corresponding
31356843Sdim// to function calls that return LLVM IR nodes.
32356843Sdimclass IRBuilderParam<int index_> { int index = index_; }
33356843Sdimclass IRBuilderAddrParam<int index_> : IRBuilderParam<index_>;
34356843Sdimclass IRBuilderIntParam<int index_, string type_> : IRBuilderParam<index_> {
35356843Sdim  string type = type_;
36356843Sdim}
37356843Sdimclass IRBuilderBase {
38356843Sdim  // The prefix of the function call, including an open parenthesis.
39356843Sdim  string prefix;
40356843Sdim
41356843Sdim  // Any parameters that have types that have to be treated specially by the
42356843Sdim  // Tablegen back end. Generally these will be types other than llvm::Value *,
43356843Sdim  // although not all other types need special treatment (e.g. llvm::Type *).
44356843Sdim  list<IRBuilderParam> special_params = [];
45356843Sdim}
46356843Sdimclass IRBuilder<string func> : IRBuilderBase {
47356843Sdim  // The usual case: a method called on the code gen function's instance of
48356843Sdim  // llvm::IRBuilder.
49356843Sdim  let prefix = "Builder." # func # "(";
50356843Sdim}
51356843Sdimclass IRFunction<string func> : IRBuilderBase {
52356843Sdim  // Some other function that doesn't use the IRBuilder at all.
53356843Sdim  let prefix = func # "(";
54356843Sdim}
55356843Sdimclass CGHelperFn<string func> : IRBuilderBase {
56356843Sdim  // A helper function defined in CGBuiltin.cpp, which takes the IRBuilder as
57356843Sdim  // an argument.
58356843Sdim  let prefix = func # "(Builder, ";
59356843Sdim}
60356843Sdimdef add: IRBuilder<"CreateAdd">;
61356843Sdimdef mul: IRBuilder<"CreateMul">;
62356843Sdimdef not: IRBuilder<"CreateNot">;
63356843Sdimdef or: IRBuilder<"CreateOr">;
64356843Sdimdef and: IRBuilder<"CreateAnd">;
65356843Sdimdef xor: IRBuilder<"CreateXor">;
66356843Sdimdef sub: IRBuilder<"CreateSub">;
67356843Sdimdef shl: IRBuilder<"CreateShl">;
68356843Sdimdef lshr: IRBuilder<"CreateLShr">;
69356843Sdimdef immshr: CGHelperFn<"MVEImmediateShr"> {
70356843Sdim  let special_params = [IRBuilderIntParam<1, "unsigned">,
71356843Sdim                        IRBuilderIntParam<2, "bool">];
72356843Sdim}
73356843Sdimdef fadd: IRBuilder<"CreateFAdd">;
74356843Sdimdef fmul: IRBuilder<"CreateFMul">;
75356843Sdimdef fsub: IRBuilder<"CreateFSub">;
76356843Sdimdef load: IRBuilder<"CreateLoad"> {
77356843Sdim  let special_params = [IRBuilderAddrParam<0>];
78356843Sdim}
79356843Sdimdef store: IRBuilder<"CreateStore"> {
80356843Sdim  let special_params = [IRBuilderAddrParam<1>];
81356843Sdim}
82356843Sdimdef xval: IRBuilder<"CreateExtractValue"> {
83356843Sdim  let special_params = [IRBuilderIntParam<1, "unsigned">];
84356843Sdim}
85356843Sdimdef ielt_const: IRBuilder<"CreateInsertElement"> {
86356843Sdim  let special_params = [IRBuilderIntParam<2, "uint64_t">];
87356843Sdim}
88356843Sdimdef ielt_var: IRBuilder<"CreateInsertElement">;
89356843Sdimdef xelt_var: IRBuilder<"CreateExtractElement">;
90356843Sdimdef trunc: IRBuilder<"CreateTrunc">;
91356843Sdimdef bitcast: IRBuilder<"CreateBitCast">;
92356843Sdimdef extend: CGHelperFn<"SignOrZeroExtend"> {
93356843Sdim  let special_params = [IRBuilderIntParam<2, "bool">];
94356843Sdim}
95356843Sdimdef zeroinit: IRFunction<"llvm::Constant::getNullValue">;
96356843Sdimdef undef: IRFunction<"UndefValue::get">;
97356843Sdimdef icmp_eq: IRBuilder<"CreateICmpEQ">;
98356843Sdimdef icmp_ne: IRBuilder<"CreateICmpNE">;
99356843Sdimdef icmp_ugt: IRBuilder<"CreateICmpUGT">;
100356843Sdimdef icmp_uge: IRBuilder<"CreateICmpUGE">;
101356843Sdimdef icmp_ult: IRBuilder<"CreateICmpULT">;
102356843Sdimdef icmp_ule: IRBuilder<"CreateICmpULE">;
103356843Sdimdef icmp_sgt: IRBuilder<"CreateICmpSGT">;
104356843Sdimdef icmp_sge: IRBuilder<"CreateICmpSGE">;
105356843Sdimdef icmp_slt: IRBuilder<"CreateICmpSLT">;
106356843Sdimdef icmp_sle: IRBuilder<"CreateICmpSLE">;
107356843Sdimdef fcmp_eq: IRBuilder<"CreateFCmpOEQ">;
108356843Sdimdef fcmp_ne: IRBuilder<"CreateFCmpUNE">; // not O: it must return true on NaNs
109356843Sdimdef fcmp_gt: IRBuilder<"CreateFCmpOGT">;
110356843Sdimdef fcmp_ge: IRBuilder<"CreateFCmpOGE">;
111356843Sdimdef fcmp_lt: IRBuilder<"CreateFCmpOLT">;
112356843Sdimdef fcmp_le: IRBuilder<"CreateFCmpOLE">;
113356843Sdimdef splat: CGHelperFn<"ARMMVEVectorSplat">;
114356843Sdimdef select: IRBuilder<"CreateSelect">;
115356843Sdim
116356843Sdim// A node that makes an Address out of a pointer-typed Value, by
117356843Sdim// providing an alignment as the second argument.
118356843Sdimdef address;
119356843Sdim
120356843Sdim// Another node class you can use in the codegen dag. This one corresponds to
121356843Sdim// an IR intrinsic function, which has to be specialized to a particular list
122356843Sdim// of types.
123356843Sdimclass IRIntBase<string name_, list<Type> params_ = [], bit appendKind_ = 0> {
124356843Sdim  string intname = name_;       // base name of the intrinsic
125356843Sdim  list<Type> params = params_;  // list of parameter types
126356843Sdim
127356843Sdim  // If this flag is set, then the IR intrinsic name will get a suffix _s, _u
128356843Sdim  // or _f depending on whether the main parameter type of the ACLE intrinsic
129356843Sdim  // being generated is a signed integer, unsigned integer, or float. Mostly
130356843Sdim  // this is useful for signed vs unsigned integers, because the ACLE
131356843Sdim  // intrinsics and the source-level integer types distinguish them, but at IR
132356843Sdim  // level the distinction has moved from the type system into the operations
133356843Sdim  // and you just have i32 or i16 etc. So when an IR intrinsic has to vary with
134356843Sdim  // signedness, you set this bit, and then you can still put the signed and
135356843Sdim  // unsigned versions in the same subclass of Intrinsic, and the Tablegen
136356843Sdim  // backend will take care of adding _s or _u as appropriate in each instance.
137356843Sdim  bit appendKind = appendKind_;
138356843Sdim}
139356843Sdim
140356843Sdim// Mostly we'll be using @llvm.arm.mve.* intrinsics, so here's a trivial
141356843Sdim// subclass that puts on that prefix.
142356843Sdimclass IRInt<string name, list<Type> params = [], bit appendKind = 0>
143356843Sdim      : IRIntBase<"arm_mve_" # name, params, appendKind>;
144356843Sdim
145356843Sdim// The 'seq' node in a codegen dag specifies a set of IR operations to be
146356843Sdim// performed in order. It has the special ability to define extra variable
147356843Sdim// names, on top of the ones that refer to the intrinsic's parameters. For
148356843Sdim// example:
149356843Sdim//
150356843Sdim//   (seq (foo this, that):$a,
151356843Sdim//        (bar this, $a):$b
152356843Sdim//        (add $a, $b))
153356843Sdim//
154356843Sdim// defines the name $a to refer to the return value of the 'foo' operation;
155356843Sdim// then the 'bar' operation uses $a as one of its arguments, and the return
156356843Sdim// value of that is assigned the name $b; finally, $a and $b are added to give
157356843Sdim// the return value of the seq construction as a whole.
158356843Sdimdef seq;
159356843Sdim
160356843Sdim// Another magic operation is 'unsignedflag', which you give a scalar
161356843Sdim// _type_ as an argument, and it expands into 1 for an unsigned type
162356843Sdim// and 0 for a signed (or floating) one.
163356843Sdimdef unsignedflag;
164356843Sdim
165356843Sdim// If you put CustomCodegen<"foo"> in an intrinsic's codegen field, it
166356843Sdim// indicates that the IR generation for that intrinsic is done by handwritten
167356843Sdim// C++ and not autogenerated at all. The effect in the MVE builtin codegen
168356843Sdim// function is to break out of the main switch and fall through to the
169356843Sdim// manual-codegen cases below it, having set the CustomCodeGenType enumerated
170356843Sdim// variable to the value given by the 'type' string here.
171356843Sdimclass CustomCodegen<string type_> { string type = type_; }
172356843Sdim
173356843Sdim// -----------------------------------------------------------------------------
174356843Sdim// System for building up complex instances of Type from simple ones.
175356843Sdim
176356843Sdim// ComplexType is used to represent any more complicated type: vectors,
177356843Sdim// multivectors, pointers etc. Its dag argument specifies how the type should
178356843Sdim// be constructed from simpler types. The operator of the dag will always be an
179356843Sdim// instance of ComplexTypeOp, defined below.
180356843Sdimclass ComplexType<dag spec_>: Type { dag spec = spec_; }
181356843Sdim
182356843Sdim// Operators you can use in the ComplexType spec dag. These are an intermediate
183356843Sdim// layer, interpreted by MveEmitter::getType() in the Tablegen backend, and
184356843Sdim// only used in the definitions below. Actual intrinsic definitions in
185356843Sdim// arm_mve.td will use the defs defined below here.
186356843Sdimclass ComplexTypeOp;
187356843Sdimdef CTO_Parameter: ComplexTypeOp;
188356843Sdimdef CTO_Vec: ComplexTypeOp;
189356843Sdimdef CTO_Pred: ComplexTypeOp;
190356843Sdimclass CTO_Tuple<int n_>: ComplexTypeOp { int n = n_; }
191356843Sdimclass CTO_Pointer<bit const_>: ComplexTypeOp { bit const = const_; }
192356843Sdimdef CTO_CopyKind: ComplexTypeOp;
193356843Sdimclass CTO_ScaleSize<int num_, int denom_>: ComplexTypeOp {
194356843Sdim  int num = num_;
195356843Sdim  int denom = denom_;
196356843Sdim}
197356843Sdim
198356843Sdim// -----------------------------------------------------------------------------
199356843Sdim// Instances of Type intended to be used directly in the specification of an
200356843Sdim// intrinsic in arm_mve.td.
201356843Sdim
202356843Sdim// The type Void can be used for the return type of an intrinsic, and as the
203356843Sdim// parameter type for intrinsics that aren't actually parameterised by any kind
204356843Sdim// of _s32 / _f16 / _u8 suffix.
205356843Sdimdef Void : Type;
206356843Sdim
207356843Sdim// A wrapper you can put on an intrinsic's argument type to prevent it from
208356843Sdim// being automatically promoted to i32 from a smaller integer type.
209356843Sdimclass unpromoted<Type t> : Type { Type underlying_type = t; }
210356843Sdim
211356843Sdim// Primitive types: base class, and an instance for the set of scalar integer
212356843Sdim// and floating types that MVE uses.
213356843Sdimclass PrimitiveType<string kind_, int size_>: Type {
214356843Sdim  string kind = kind_;
215356843Sdim  int size = size_;
216356843Sdim  string nameOverride = "";
217356843Sdim}
218356843Sdim
219356843Sdim// The type records defined by these foreaches have names like s32, f16, u8.
220356843Sdimforeach size = [8, 16, 32, 64] in
221356843Sdim  foreach kind = ["u", "s"] in
222356843Sdim    def kind # size: PrimitiveType<kind, size>;
223356843Sdimforeach size = [16, 32] in
224356843Sdim  foreach kind = ["f"] in
225356843Sdim    def kind # size: PrimitiveType<kind, size>;
226356843Sdim
227356843Sdim// Sometimes we need to refer to a type by a different name in C, when
228356843Sdim// ACLE defines a function parameter to be something like 'unsigned'
229356843Sdim// rather than uint32_t.
230356843Sdimdef uint: PrimitiveType<"u", 32> { let nameOverride = "unsigned"; }
231356843Sdimdef sint: PrimitiveType<"s", 32> { let nameOverride = "int"; }
232356843Sdim
233356843Sdim// VecOf<t> expects t to be a scalar, and gives a 128-bit vector of whatever it
234356843Sdim// is.
235356843Sdimclass VecOf<Type t>: ComplexType<(CTO_Vec t)>;
236356843Sdim
237356843Sdim// NarrowedVecOf<t,v> expects t to be a scalar type, and v to be a vector
238356843Sdim// type. It returns a vector type whose element type is t, and whose lane
239356843Sdim// count is the same as the lane count of v. (Used as an intermediate value
240356843Sdim// type in the IR representation of a widening load: you load a vector of
241356843Sdim// small things out of memory, and then zext/sext them into a full 128-bit
242356843Sdim// output vector.)
243356843Sdimclass NarrowedVecOf<Type t, Type v>: ComplexType<(CTO_Vec t, v)>;
244356843Sdim
245356843Sdim// PredOf expects t to be a scalar, and expands to a predicate vector which
246356843Sdim// (logically speaking) has the same number of lanes as VecOf<t> would.
247356843Sdimclass PredOf<Type t>: ComplexType<(CTO_Pred t)>;
248356843Sdim
249356843Sdim// Scalar expands to whatever is the main parameter type of the current
250356843Sdim// intrinsic. Vector and Predicate expand to the vector and predicate types
251356843Sdim// corresponding to that.
252356843Sdimdef Scalar: ComplexType<(CTO_Parameter)>;
253356843Sdimdef Vector: VecOf<Scalar>;
254356843Sdimdef Predicate: PredOf<Scalar>;
255356843Sdim
256356843Sdim// MultiVector<n> expands to a type containing n instances of Vector. (There's
257356843Sdim// no need to define this for a general underlying vector type, since it's only
258356843Sdim// used by vld2q and friends, which don't need that generality.)
259356843Sdimclass MultiVector<int n>: ComplexType<(CTO_Tuple<n> Vector)>;
260356843Sdim
261356843Sdim// Ptr<t> and CPtr<t> expand to a pointer to t, or a pointer to const t,
262356843Sdim// respectively.
263356843Sdimclass Ptr<Type t>: ComplexType<(CTO_Pointer<0> t)>;
264356843Sdimclass CPtr<Type t>: ComplexType<(CTO_Pointer<1> t)>;
265356843Sdim
266356843Sdim// CopyKind<s,k> expects s and k to be scalar types. It returns a scalar type
267356843Sdim// whose kind (signed, unsigned or float) matches that of k, and whose size
268356843Sdim// matches that of s.
269356843Sdimclass CopyKind<Type s, Type k>: ComplexType<(CTO_CopyKind s, k)>;
270356843Sdim
271356843Sdim// DoubleSize<k> expects k to be a scalar type. It returns a scalar type
272356843Sdim// whose kind (signed, unsigned or float) matches that of k, and whose size
273356843Sdim// is double that of k, if possible.
274356843Sdimclass DoubleSize<Type k> : ComplexType<(CTO_ScaleSize<2, 1> k)>;
275356843Sdimclass HalfSize<Type k>   : ComplexType<(CTO_ScaleSize<1, 2> k)>;
276356843Sdim
277356843Sdim// Unsigned<t> expects t to be a scalar type, and expands to the unsigned
278356843Sdim// integer scalar of the same size. So it returns u16 if you give it s16 or
279356843Sdim// f16 (or u16 itself). Similarly, Signed<t> makes the type signed.
280356843Sdimclass Unsigned<Type t>: ComplexType<(CTO_CopyKind t, u32)>;
281356843Sdimclass Signed<Type t>: ComplexType<(CTO_CopyKind t, s32)>;
282356843Sdim
283356843Sdim// UScalar and UVector expand to the unsigned-integer versions of
284356843Sdim// Scalar and Vector. SScalar and SVector are signed-integer versions.
285356843Sdimdef UScalar: Unsigned<Scalar>;
286356843Sdimdef UVector: VecOf<UScalar>;
287356843Sdimdef SScalar: Signed<Scalar>;
288356843Sdimdef SVector: VecOf<SScalar>;
289356843Sdim
290356843Sdim// DblVector expands to a vector of scalars of size twice the size of Scalar.
291356843Sdim// HalfVector, similarly, expands to a vector of half-sized scalars. And
292356843Sdim// UHalfVector is a vector of half-sized _unsigned integers_.
293356843Sdimdef DblVector: VecOf<DoubleSize<Scalar>>;
294356843Sdimdef HalfVector: VecOf<HalfSize<Scalar>>;
295356843Sdimdef UHalfVector: VecOf<Unsigned<HalfSize<Scalar>>>;
296356843Sdim
297356843Sdim// Expands to the 32-bit integer of the same signedness as Scalar.
298356843Sdimdef Scalar32: CopyKind<u32, Scalar>;
299356843Sdim// Expands to the 64-bit integer of the same signedness as Scalar.
300356843Sdimdef Scalar64: CopyKind<u64, Scalar>;
301356843Sdim
302356843Sdim// -----------------------------------------------------------------------------
303356843Sdim// Internal definitions for specifying immediate arguments for an intrinsic.
304356843Sdim
305356843Sdimclass ImmediateBounds;
306356843Sdimclass Immediate<Type type_, ImmediateBounds bounds_>: Type {
307356843Sdim  Type type = type_;
308356843Sdim  ImmediateBounds bounds = bounds_;
309356843Sdim  string extra;
310356843Sdim  string extraarg;
311356843Sdim}
312356843Sdimclass IB_ConstRange<int lo_, int hi_> : ImmediateBounds {
313356843Sdim  int lo = lo_;
314356843Sdim  int hi = hi_;
315356843Sdim}
316356843Sdimdef IB_UEltValue : ImmediateBounds;
317356843Sdimdef IB_LaneIndex : ImmediateBounds;
318356843Sdimclass IB_EltBit<int base_, Type type_ = Scalar> : ImmediateBounds {
319356843Sdim  int base = base_;
320356843Sdim  Type type = type_;
321356843Sdim}
322356843Sdim
323356843Sdim// -----------------------------------------------------------------------------
324356843Sdim// End-user definitions for immediate arguments.
325356843Sdim
326356843Sdim// imm_simd and imm_simd_restrictive are used for the immediate operands to
327356843Sdim// intrinsics like vmvnq or vorrq. imm_simd_restrictive has to be an 8-bit
328356843Sdim// value shifted left by a whole number of bytes; imm_simd_vmvn can also be of
329356843Sdim// the form 0xXXFF for some byte value XX.
330356843Sdimdef imm_simd_restrictive : Immediate<u32, IB_UEltValue> {
331356843Sdim  let extra = "ShiftedByte";
332356843Sdim}
333356843Sdimdef imm_simd_vmvn : Immediate<u32, IB_UEltValue> {
334356843Sdim  let extra = "ShiftedByteOrXXFF";
335356843Sdim}
336356843Sdim
337356843Sdim// imm_1toN can take any value from 1 to N inclusive, where N is the number of
338356843Sdim// bits in the main parameter type. (E.g. an immediate shift count, in an
339356843Sdim// intrinsic that shifts every lane of a vector by the same amount.)
340356843Sdim//
341356843Sdim// imm_0toNm1 is the same but with the range offset by 1, i.e. 0 to N-1
342356843Sdim// inclusive.
343356843Sdim//
344356843Sdim// imm_1toHalfN is like imm_1toN, but applied to a half-width type.
345356843Sdim// (So if Scalar is s16, for example, it'll give you the range 1 to 8.)
346356843Sdimdef imm_1toN : Immediate<sint, IB_EltBit<1>>;
347356843Sdimdef imm_0toNm1 : Immediate<sint, IB_EltBit<0>>;
348356843Sdimdef imm_1toHalfN : Immediate<sint, IB_EltBit<1, HalfSize<Scalar>>>;
349356843Sdim
350356843Sdim// imm_lane has to be the index of a vector lane in the main vector type, i.e
351356843Sdim// it can range from 0 to (128 / size of scalar)-1 inclusive. (e.g. vgetq_lane)
352356843Sdimdef imm_lane : Immediate<sint, IB_LaneIndex>;
353356843Sdim
354356843Sdim// imm_1to32 can be in the range 1 to 32, unconditionally. (e.g. scalar shift
355356843Sdim// intrinsics)
356356843Sdimdef imm_1to32 : Immediate<sint, IB_ConstRange<1, 32>>;
357356843Sdim
358356843Sdim// imm_1248 can be 1, 2, 4 or 8. (e.g. vidupq)
359356843Sdimdef imm_1248 : Immediate<u32, IB_ConstRange<1, 8>> {
360356843Sdim  let extra = "Power2";
361356843Sdim}
362356843Sdim
363356843Sdim// imm_mem7bit<n> is a valid immediate offset for a load/store intrinsic whose
364356843Sdim// memory access size is n bytes (e.g. 1 for vldrb_[whatever], 2 for vldrh,
365356843Sdim// ...). The set of valid immediates for these is {-127*n, ..., -1*n, 0*n, 1*n,
366356843Sdim// ..., 127*n}.
367356843Sdimclass imm_mem7bit<int membytes>
368356843Sdim  : Immediate<sint, IB_ConstRange<!mul(membytes, -127), !mul(membytes, 127)>> {
369356843Sdim  let extra = !if(!eq(membytes, 1), ?, "Multiple");
370356843Sdim  let extraarg = !cast<string>(membytes);
371356843Sdim}
372356843Sdim
373356843Sdim// -----------------------------------------------------------------------------
374356843Sdim// Specification of ways that the full name of an intrinsic can be mapped to
375356843Sdim// its shorter polymorphic name.
376356843Sdim
377356843Sdimclass PolymorphicNameType<int nt_, string x_> {
378356843Sdim  int NumTypeSuffixesToDiscard = nt_;
379356843Sdim  string ExtraSuffixToDiscard = x_;
380356843Sdim}
381356843Sdim
382356843Sdim// PNT_None: the intrinsic is not polymorphic at all, so its short name is the
383356843Sdim// same as its long name. (E.g. scalar shift intrinsics such as uqshl.)
384356843Sdimdef PNT_None:   PolymorphicNameType<0, ?>;
385356843Sdim
386356843Sdim// PNT_Type: the usual case, in which the polymorphic name is made by dropping
387356843Sdim// the type suffix, so it ends up the same as the Tablegen record name. E.g.
388356843Sdim// vaddq_u16 -> vaddq.
389356843Sdimdef PNT_Type:   PolymorphicNameType<1, ?>;
390356843Sdim
391356843Sdim// PNT_2Type: the polymorphic name is made by dropping _two_ type suffixes.
392356843Sdim// E.g. vcvtq_f16_u16 -> vcvtq.
393356843Sdimdef PNT_2Type:  PolymorphicNameType<2, ?>;
394356843Sdim
395356843Sdim// PNT_NType: the polymorphic name is made by dropping an "_n" suffix and a
396356843Sdim// type. E.g. vaddq_n_u16 -> vaddq.
397356843Sdimdef PNT_NType:  PolymorphicNameType<1, "n">;
398356843Sdim
399356843Sdim// PNT_NType: the polymorphic name is made by just dropping an "_n" suffix
400356843Sdim// (even if it isn't at the end of the name). E.g. vidupq_n_u16 -> vidupq_u16.
401356843Sdimdef PNT_N:      PolymorphicNameType<0, "n">;
402356843Sdim
403356843Sdim// PNT_WBType: the polymorphic name is made by dropping an "_wb" suffix and a
404356843Sdim// type. E.g. vidupq_m_wb_u16 -> vidupq_m.
405356843Sdimdef PNT_WBType: PolymorphicNameType<1, "wb">;
406356843Sdim
407356843Sdim// PNT_WB: the polymorphic name is made by just dropping "_wb". E.g.
408356843Sdim// vidupq_wb_u16 -> vidupq_u16.
409356843Sdimdef PNT_WB:     PolymorphicNameType<0, "wb">;
410356843Sdim
411356843Sdim// -----------------------------------------------------------------------------
412356843Sdim// The main class Intrinsic. Define one of these for each family of ACLE
413356843Sdim// intrinsics which are the same apart from some final type suffix (e.g.
414356843Sdim// vaddq_{s8,u8,f16,...}.
415356843Sdim//
416356843Sdim// The record's name plus that type suffix is taken to be the full unambiguous
417356843Sdim// name of the function. Its shorter polymorphic name is constructed from that
418356843Sdim// in turn, in a way specified by the PolymorphicNameType system above.
419356843Sdim
420356843Sdimclass Intrinsic<Type ret_, dag args_, dag codegen_> {
421356843Sdim  // List of parameter types to suffix to this intrinsic's name. A separate
422356843Sdim  // actual ACLE intrinsic will be generated for each of these. Set it to
423356843Sdim  // [Void] if the intrinsic is not polymorphic at all.
424356843Sdim  list<Type> params;
425356843Sdim
426356843Sdim  // Return type and arguments for the intrinsic.
427356843Sdim  Type ret = ret_;
428356843Sdim  dag args = args_;
429356843Sdim
430356843Sdim  // Specification of how to generate its IR.
431356843Sdim  dag codegen = codegen_;
432356843Sdim
433356843Sdim  // Default to PNT_Type, which is by far the most common case.
434356843Sdim  PolymorphicNameType pnt = PNT_Type;
435356843Sdim
436356843Sdim  // A very few intrinsics _only_ have a polymorphic name.
437356843Sdim  bit polymorphicOnly = 0;
438356843Sdim
439356843Sdim  // True if the builtin has to avoid evaluating its arguments.
440356843Sdim  bit nonEvaluating = 0;
441356843Sdim
442356843Sdim  // Use to override the suffix letter to make e.g.vfooq_p16
443356843Sdim  // with an override suffix letter of "p".
444356843Sdim  string overrideKindLetter = "";
445356843Sdim}
446356843Sdim
447356843Sdim// Sometimes you have to use two separate Intrinsic declarations to
448356843Sdim// declare intrinsics that are logically the same family (e.g. vaddq,
449356843Sdim// because it needs to expand to an Add or FAdd IR node depending on
450356843Sdim// type). For that purpose, you can derive from NameOverride to
451356843Sdim// specify the intrinsic's base name independently of the Tablegen
452356843Sdim// record name.
453356843Sdim
454356843Sdimclass NameOverride<string basename_> {
455356843Sdim  string basename = basename_;
456356843Sdim}
457356843Sdim
458356843Sdim// A wrapper to define both _m and _x versions of a predicated
459356843Sdim// intrinsic.
460356843Sdimmulticlass IntrinsicMX<Type rettype, dag arguments, dag cg,
461356843Sdim                       int wantXVariant = 1,
462356843Sdim                       string nameSuffix = "",
463356843Sdim                       PolymorphicNameType pnt_x = PNT_Type> {
464356843Sdim  // The _m variant takes an initial parameter called $inactive, which
465356843Sdim  // provides the input value of the output register, i.e. all the
466356843Sdim  // inactive lanes in the predicated operation take their values from
467356843Sdim  // this.
468356843Sdim  def "_m" # nameSuffix:
469356843Sdim     Intrinsic<rettype, !con((args rettype:$inactive), arguments), cg>;
470356843Sdim
471356843Sdim  foreach unusedVar = !if(!eq(wantXVariant, 1), [1], []<int>) in {
472356843Sdim    // The _x variant leaves off that parameter, and simply uses an
473356843Sdim    // undef value of the same type.
474356843Sdim    def "_x" # nameSuffix:
475356843Sdim       Intrinsic<rettype, arguments, (seq (undef rettype):$inactive, cg)> {
476356843Sdim      // Allow overriding of the polymorphic name type, because
477356843Sdim      // sometimes the _m and _x variants polymorph differently
478356843Sdim      // (typically because the type of the inactive parameter can be
479356843Sdim      // used as a disambiguator if it's present).
480356843Sdim      let pnt = pnt_x;
481356843Sdim    }
482356843Sdim  }
483356843Sdim}
484356843Sdim
485356843Sdim// -----------------------------------------------------------------------------
486356843Sdim// Convenience lists of parameter types. 'T' is just a container record, so you
487356843Sdim// can define a typical intrinsic with 'let Params = T.Usual', or similar,
488356843Sdim// instead of having to repeat a long list every time.
489356843Sdim
490356843Sdimdef T {
491356843Sdim  list<Type> Signed = [s8, s16, s32];
492356843Sdim  list<Type> Unsigned = [u8, u16, u32];
493356843Sdim  list<Type> Int = Signed # Unsigned;
494356843Sdim  list<Type> Float = [f16, f32];
495356843Sdim  list<Type> Usual = Int # Float;
496356843Sdim  list<Type> Int8 = [s8, u8];
497356843Sdim  list<Type> Int16 = [s16, u16];
498356843Sdim  list<Type> Int32 = [s32, u32];
499356843Sdim  list<Type> Int64 = [s64, u64];
500356843Sdim  list<Type> Poly = [u8, u16]; // Actually p8 and p16
501356843Sdim  list<Type> All8 = Int8;
502356843Sdim  list<Type> All16 = Int16 # [f16];
503356843Sdim  list<Type> All32 = Int32 # [f32];
504356843Sdim  list<Type> All64 = Int64;
505356843Sdim  list<Type> All = Usual # All64;
506356843Sdim}
507356843Sdim
508356843Sdim// -----------------------------------------------------------------------------
509356843Sdim// Container record for DAG constant values. These constants are used because
510356843Sdim// bit/int class/multiclass parameters cannot be used to produce a dag node:
511356843Sdim// for example (u32 x) where x is 0 is transformed into (u32 { 0 }) by the
512356843Sdim// Tablegen parser.
513356843Sdimdef V {
514356843Sdim  dag False = (u32 0);
515356843Sdim  dag True  = (u32 1);
516356843Sdim}
517