| //==------ riscv_vector_common.td - RISC-V V-ext builtin class ------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines RVV builtin base class for RISC-V V-extension. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| //===----------------------------------------------------------------------===// |
| // Instruction definitions |
| //===----------------------------------------------------------------------===// |
| // Each record of the class RVVBuiltin defines a collection of builtins (i.e. |
| // "def vadd : RVVBuiltin" will be used to define things like "vadd_vv_i32m1", |
| // "vadd_vv_i32m2", etc). |
| // |
| // The elements of this collection are defined by an instantiation process the |
| // range of which is specified by the cross product of the LMUL attribute and |
| // every element in the attribute TypeRange. By default builtins have LMUL = [1, |
| // 2, 4, 8, 1/2, 1/4, 1/8] so the process is repeated 7 times. In tablegen we |
| // use the Log2LMUL [0, 1, 2, 3, -1, -2, -3] to represent the LMUL. |
| // |
| // LMUL represents the fact that the types of values used by that builtin are |
| // values generated by instructions that are executed under that LMUL. However, |
| // this does not mean the builtin is necessarily lowered into an instruction |
| // that executes under the specified LMUL. An example where this happens are |
| // loads and stores of masks. A mask like `vbool8_t` can be generated, for |
| // instance, by comparing two `__rvv_int8m1_t` (this is LMUL=1) or comparing two |
| // `__rvv_int16m2_t` (this is LMUL=2). The actual load or store, however, will |
| // be performed under LMUL=1 because mask registers are not grouped. |
| // |
| // TypeRange is a non-empty sequence of basic types: |
| // |
| // c: int8_t (i8) |
| // s: int16_t (i16) |
| // i: int32_t (i32) |
| // l: int64_t (i64) |
| // x: float16_t (half) |
| // f: float32_t (float) |
| // d: float64_t (double) |
| // |
| // This way, given an LMUL, a record with a TypeRange "sil" will cause the |
| // definition of 3 builtins. Each type "t" in the TypeRange (in this example |
| // they are int16_t, int32_t, int64_t) is used as a parameter that drives the |
| // definition of that particular builtin (for the given LMUL). |
| // |
| // During the instantiation, types can be transformed or modified using type |
| // transformers. Given a type "t" the following primitive type transformers can |
| // be applied to it to yield another type. |
| // |
| // e: type of "t" as is (identity) |
| // v: computes a vector type whose element type is "t" for the current LMUL |
| // w: computes a vector type identical to what 'v' computes except for the |
| // element type which is twice as wide as the element type of 'v' |
| // q: computes a vector type identical to what 'v' computes except for the |
| // element type which is four times as wide as the element type of 'v' |
| // o: computes a vector type identical to what 'v' computes except for the |
| // element type which is eight times as wide as the element type of 'v' |
| // m: computes a vector type identical to what 'v' computes except for the |
| // element type which is bool |
| // 0: void type, ignores "t" |
| // z: size_t, ignores "t" |
| // t: ptrdiff_t, ignores "t" |
| // u: unsigned long, ignores "t" |
| // l: long, ignores "t" |
| // |
| // So for instance if t is "i", i.e. int, then "e" will yield int again. "v" |
| // will yield an RVV vector type (assume LMUL=1), so __rvv_int32m1_t. |
| // Accordingly "w" would yield __rvv_int64m2_t. |
| // |
| // A type transformer can be prefixed by other non-primitive type transformers. |
| // |
| // P: constructs a pointer to the current type |
| // C: adds const to the type |
| // K: requires the integer type to be a constant expression |
| // U: given an integer type or vector type, computes its unsigned variant |
| // I: given a vector type, compute the vector type with integer type |
| // elements of the same width |
| // F: given a vector type, compute the vector type with floating-point type |
| // elements of the same width |
| // S: given a vector type, computes its equivalent one for LMUL=1. This is a |
| // no-op if the vector was already LMUL=1 |
| // (Log2EEW:Value): Log2EEW value could be 3/4/5/6 (8/16/32/64), given a |
| // vector type (SEW and LMUL) and EEW (8/16/32/64), computes its |
| // equivalent integer vector type with EEW and corresponding ELMUL (elmul = |
| // (eew/sew) * lmul). For example, vector type is __rvv_float16m4 |
| // (SEW=16, LMUL=4) and Log2EEW is 3 (EEW=8), and then equivalent vector |
| // type is __rvv_uint8m2_t (elmul=(8/16)*4 = 2). Ignore to define a new |
| // builtins if its equivalent type has illegal lmul. |
| // (FixedSEW:Value): Given a vector type (SEW and LMUL), and computes another |
| // vector type which only changed SEW as given value. Ignore to define a new |
| // builtin if its equivalent type has illegal lmul or the SEW does not changed. |
| // (SFixedLog2LMUL:Value): Smaller Fixed Log2LMUL. Given a vector type (SEW |
| // and LMUL), and computes another vector type which only changed LMUL as |
| // given value. The new LMUL should be smaller than the old one. Ignore to |
| // define a new builtin if its equivalent type has illegal lmul. |
| // (LFixedLog2LMUL:Value): Larger Fixed Log2LMUL. Given a vector type (SEW |
| // and LMUL), and computes another vector type which only changed LMUL as |
| // given value. The new LMUL should be larger than the old one. Ignore to |
| // define a new builtin if its equivalent type has illegal lmul. |
| // |
| // Following with the example above, if t is "i", then "Ue" will yield unsigned |
| // int and "Fv" will yield __rvv_float32m1_t (again assuming LMUL=1), Fw would |
| // yield __rvv_float64m2_t, etc. |
| // |
| // Each builtin is then defined by applying each type in TypeRange against the |
| // sequence of type transformers described in Suffix and Prototype. |
| // |
| // The name of the builtin is defined by the Name attribute (which defaults to |
| // the name of the class) appended (separated with an underscore) the Suffix |
| // attribute. For instance with Name="foo", Suffix = "v" and TypeRange = "il", |
| // the builtin generated will be __builtin_rvv_foo_i32m1 and |
| // __builtin_rvv_foo_i64m1 (under LMUL=1). If Suffix contains more than one |
| // type transformer (say "vv") each of the types is separated with an |
| // underscore as in "__builtin_rvv_foo_i32m1_i32m1". |
| // |
| // The C/C++ prototype of the builtin is defined by the Prototype attribute. |
| // Prototype is a non-empty sequence of type transformers, the first of which |
| // is the return type of the builtin and the rest are the parameters of the |
| // builtin, in order. For instance if Prototype is "wvv" and TypeRange is "si" |
| // a first builtin will have type |
| // __rvv_int32m2_t (__rvv_int16m1_t, __rvv_int16m1_t) and the second builtin |
| // will have type __rvv_int64m2_t (__rvv_int32m1_t, __rvv_int32m1_t) (again |
| // under LMUL=1). |
| // |
| // There are a number of attributes that are used to constraint the number and |
| // shape of the builtins generated. Refer to the comments below for them. |
| |
| class PolicyScheme<int val>{ |
| int Value = val; |
| } |
| def NonePolicy : PolicyScheme<0>; |
| def HasPassthruOperand : PolicyScheme<1>; |
| def HasPolicyOperand : PolicyScheme<2>; |
| |
| class RVVBuiltin<string suffix, string prototype, string type_range, |
| string overloaded_suffix = ""> { |
| // Base name that will be prepended in __builtin_rvv_ and appended the |
| // computed Suffix. |
| string Name = NAME; |
| |
| // If not empty, each instantiated builtin will have this appended after an |
| // underscore (_). It is instantiated like Prototype. |
| string Suffix = suffix; |
| |
| // If empty, default OverloadedName is sub string of `Name` which end of first |
| // '_'. For example, the default overloaded name is `vadd` for Name `vadd_vv`. |
| // It's used for describe some special naming cases. |
| string OverloadedName = ""; |
| |
| // If not empty, each OverloadedName will have this appended after an |
| // underscore (_). It is instantiated like Prototype. |
| string OverloadedSuffix = overloaded_suffix; |
| |
| // The different variants of the builtin, parameterised with a type. |
| string TypeRange = type_range; |
| |
| // We use each type described in TypeRange and LMUL with prototype to |
| // instantiate a specific element of the set of builtins being defined. |
| // Prototype attribute defines the C/C++ prototype of the builtin. It is a |
| // non-empty sequence of type transformers, the first of which is the return |
| // type of the builtin and the rest are the parameters of the builtin, in |
| // order. For instance if Prototype is "wvv", TypeRange is "si" and LMUL=1, a |
| // first builtin will have type |
| // __rvv_int32m2_t (__rvv_int16m1_t, __rvv_int16m1_t), and the second builtin |
| // will have type __rvv_int64m2_t (__rvv_int32m1_t, __rvv_int32m1_t). |
| string Prototype = prototype; |
| |
| // This builtin has a masked form. |
| bit HasMasked = true; |
| |
| // If HasMasked, this flag states that this builtin has a maskedoff operand. It |
| // is always the first operand in builtin and IR intrinsic. |
| bit HasMaskedOffOperand = true; |
| |
| // This builtin has a granted vector length parameter. |
| bit HasVL = true; |
| |
| // The policy scheme for masked intrinsic IR. |
| // It could be NonePolicy or HasPolicyOperand. |
| // HasPolicyOperand: Has a policy operand. 0 is tail and mask undisturbed, 1 is |
| // tail agnostic, 2 is mask undisturbed, and 3 is tail and mask agnostic. The |
| // policy operand is located at the last position. |
| PolicyScheme MaskedPolicyScheme = HasPolicyOperand; |
| |
| // The policy scheme for unmasked intrinsic IR. |
| // It could be NonePolicy, HasPassthruOperand or HasPolicyOperand. |
| // HasPassthruOperand: Has a passthru operand to decide tail policy. If it is |
| // poison, tail policy is tail agnostic, otherwise policy is tail undisturbed. |
| // HasPolicyOperand: Has a policy operand. 1 is tail agnostic and 0 is tail |
| // undisturbed. |
| PolicyScheme UnMaskedPolicyScheme = NonePolicy; |
| |
| // This builtin support tail agnostic and undisturbed policy. |
| bit HasTailPolicy = true; |
| // This builtin support mask agnostic and undisturbed policy. |
| bit HasMaskPolicy = true; |
| |
| // This builtin prototype with TA or TAMA policy could not support overloading |
| // API. Other policy intrinsic functions would support overloading API with |
| // suffix `_tu`, `tumu`, `tuma`, `tamu` and `tama`. |
| bit SupportOverloading = true; |
| |
| // This builtin is valid for the given Log2LMULs. |
| list<int> Log2LMUL = [0, 1, 2, 3, -1, -2, -3]; |
| |
| // Manual code in clang codegen riscv_vector_builtin_cg.inc |
| code ManualCodegen = [{}]; |
| |
| // When emit the automatic clang codegen, it describes what types we have to use |
| // to obtain the specific LLVM intrinsic. -1 means the return type, otherwise, |
| // k >= 0 meaning the k-th operand (counting from zero) of the codegen'd |
| // parameter of the unmasked version. k can't be the mask operand's position. |
| list<int> IntrinsicTypes = []; |
| |
| // If these names are not empty, this is the ID of the LLVM intrinsic |
| // we want to lower to. |
| string IRName = NAME; |
| |
| // If HasMasked, this is the ID of the LLVM intrinsic we want to lower to. |
| string MaskedIRName = NAME #"_mask"; |
| |
| // Use clang_builtin_alias to save the number of builtins. |
| bit HasBuiltinAlias = true; |
| |
| // Features required to enable for this builtin. |
| list<string> RequiredFeatures = []; |
| |
| // Number of fields for Load/Store Segment instructions. |
| int NF = 1; |
| |
| // Set to true if the builtin is associated with tuple types. |
| bit IsTuple = false; |
| } |
| |
| // This is the code emitted in the header. |
| class RVVHeader { |
| code HeaderCode; |
| } |