//==------ 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) // y: bfloat16_t (bfloat16) // // 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" // f: float32, 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. // (SEFixedLog2LMUL:Value): Smaller or Equal 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 or // equal to 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 Value = val; } def NonePolicy : PolicyScheme<0>; def HasPassthruOperand : PolicyScheme<1>; def HasPolicyOperand : PolicyScheme<2>; class RVVBuiltin { // 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 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 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 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; // Set to true if the builtin has a parameter that models floating-point // rounding mode control bit HasFRMRoundModeOp = false; } // This is the code emitted in the header. class RVVHeader { code HeaderCode; } //===----------------------------------------------------------------------===// // Basic classes with automatic codegen. //===----------------------------------------------------------------------===// class RVVOutBuiltin : RVVBuiltin { let IntrinsicTypes = [-1]; } class RVVOp0Builtin : RVVBuiltin { let IntrinsicTypes = [0]; } class RVVOutOp1Builtin : RVVBuiltin { let IntrinsicTypes = [-1, 1]; } class RVVOutOp0Op1Builtin : RVVBuiltin { let IntrinsicTypes = [-1, 0, 1]; } multiclass RVVBuiltinSet> suffixes_prototypes, list intrinsic_types> { let IRName = intrinsic_name, MaskedIRName = intrinsic_name # "_mask", IntrinsicTypes = intrinsic_types in { foreach s_p = suffixes_prototypes in { let Name = NAME # "_" # s_p[0] in { defvar suffix = s_p[1]; defvar prototype = s_p[2]; def : RVVBuiltin; } } } } // IntrinsicTypes is output, op0, op1 [-1, 0, 1] multiclass RVVOutOp0Op1BuiltinSet> suffixes_prototypes> : RVVBuiltinSet; multiclass RVVOutBuiltinSet> suffixes_prototypes> : RVVBuiltinSet; multiclass RVVOp0BuiltinSet> suffixes_prototypes> : RVVBuiltinSet; // IntrinsicTypes is output, op1 [-1, 0] multiclass RVVOutOp0BuiltinSet> suffixes_prototypes> : RVVBuiltinSet; // IntrinsicTypes is output, op1 [-1, 1] multiclass RVVOutOp1BuiltinSet> suffixes_prototypes> : RVVBuiltinSet; multiclass RVVOp0Op1BuiltinSet> suffixes_prototypes> : RVVBuiltinSet; multiclass RVVOutOp1Op2BuiltinSet> suffixes_prototypes> : RVVBuiltinSet; // IntrinsicTypes is output, op2 [-1, 2] multiclass RVVOutOp2BuiltinSet> suffixes_prototypes> : RVVBuiltinSet; multiclass RVVSignedBinBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVSignedBinBuiltinSetRoundingMode : RVVOutOp1BuiltinSet; multiclass RVVUnsignedBinBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVUnsignedBinBuiltinSetRoundingMode : RVVOutOp1BuiltinSet; multiclass RVVIntBinBuiltinSet : RVVSignedBinBuiltinSet, RVVUnsignedBinBuiltinSet; multiclass RVVInt64BinBuiltinSet : RVVOutOp1BuiltinSet, RVVOutOp1BuiltinSet; multiclass RVVSlideOneBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVSignedShiftBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVSignedShiftBuiltinSetRoundingMode : RVVOutOp1BuiltinSet; multiclass RVVUnsignedShiftBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVUnsignedShiftBuiltinSetRoundingMode : RVVOutOp1BuiltinSet; multiclass RVVShiftBuiltinSet : RVVSignedShiftBuiltinSet, RVVUnsignedShiftBuiltinSet; let Log2LMUL = [-3, -2, -1, 0, 1, 2] in { multiclass RVVSignedNShiftBuiltinSet : RVVOutOp0Op1BuiltinSet; multiclass RVVSignedNShiftBuiltinSetRoundingMode : RVVOutOp0Op1BuiltinSet; multiclass RVVUnsignedNShiftBuiltinSet : RVVOutOp0Op1BuiltinSet; multiclass RVVUnsignedNShiftBuiltinSetRoundingMode : RVVOutOp0Op1BuiltinSet; } multiclass RVVCarryinBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVCarryOutInBuiltinSet : RVVOp0Op1BuiltinSet; multiclass RVVSignedMaskOutBuiltinSet : RVVOp0Op1BuiltinSet; multiclass RVVUnsignedMaskOutBuiltinSet : RVVOp0Op1BuiltinSet; multiclass RVVIntMaskOutBuiltinSet : RVVSignedMaskOutBuiltinSet, RVVUnsignedMaskOutBuiltinSet; class RVVIntExt : RVVBuiltin { let IRName = intrinsic_name; let MaskedIRName = intrinsic_name # "_mask"; let OverloadedName = NAME; let IntrinsicTypes = [-1, 0]; } let HasMaskedOffOperand = false in { multiclass RVVIntTerBuiltinSet { defm "" : RVVOutOp1BuiltinSet; } multiclass RVVFloatingTerBuiltinSet { defm "" : RVVOutOp1BuiltinSet; } multiclass RVVFloatingTerBuiltinSetRoundingMode { defm "" : RVVOutOp1BuiltinSet; } } let HasMaskedOffOperand = false, Log2LMUL = [-2, -1, 0, 1, 2] in { multiclass RVVFloatingWidenTerBuiltinSet { defm "" : RVVOutOp1Op2BuiltinSet; } multiclass RVVFloatingWidenTerBuiltinSetRoundingMode { defm "" : RVVOutOp1Op2BuiltinSet; } } multiclass RVVFloatingBinBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVFloatingBinBuiltinSetRoundingMode : RVVOutOp1BuiltinSet; multiclass RVVFloatingBinVFBuiltinSet : RVVOutOp1BuiltinSet; multiclass RVVFloatingBinVFBuiltinSetRoundingMode : RVVOutOp1BuiltinSet; multiclass RVVFloatingMaskOutBuiltinSet : RVVOp0Op1BuiltinSet; multiclass RVVFloatingMaskOutVFBuiltinSet : RVVOp0Op1BuiltinSet; multiclass RVVConvBuiltinSet> suffixes_prototypes> { let Name = intrinsic_name, IRName = intrinsic_name, MaskedIRName = intrinsic_name # "_mask", IntrinsicTypes = [-1, 0] in { foreach s_p = suffixes_prototypes in { defvar suffix = s_p[0]; defvar prototype = s_p[1]; def : RVVBuiltin; } } } class RVVMaskBinBuiltin : RVVOutBuiltin<"m", "mmm", "c"> { let Name = NAME # "_mm"; let HasMasked = false; } class RVVMaskUnaryBuiltin : RVVOutBuiltin<"m", "mm", "c"> { let Name = NAME # "_m"; } class RVVMaskNullaryBuiltin : RVVOutBuiltin<"m", "m", "c"> { let Name = NAME # "_m"; let HasMasked = false; let SupportOverloading = false; } class RVVMaskOp0Builtin : RVVOp0Builtin<"m", prototype, "c"> { let Name = NAME # "_m"; let HasMaskedOffOperand = false; } let UnMaskedPolicyScheme = HasPolicyOperand, HasMaskedOffOperand = false in { multiclass RVVSlideUpBuiltinSet { defm "" : RVVOutBuiltinSet; defm "" : RVVOutBuiltinSet; } } let UnMaskedPolicyScheme = HasPassthruOperand, ManualCodegen = [{ if (IsMasked) { std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1); if ((PolicyAttrs & RVV_VTA) && (PolicyAttrs & RVV_VMA)) Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType)); } else { if (PolicyAttrs & RVV_VTA) Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType)); } Ops.push_back(ConstantInt::get(Ops.back()->getType(), PolicyAttrs)); IntrinsicTypes = {ResultType, Ops.back()->getType()}; }] in { multiclass RVVSlideDownBuiltinSet { defm "" : RVVOutBuiltinSet; defm "" : RVVOutBuiltinSet; } } class RVVFloatingUnaryBuiltin : RVVOutBuiltin { let Name = NAME # "_" # builtin_suffix; } class RVVFloatingUnaryVVBuiltin : RVVFloatingUnaryBuiltin<"v", "v", "vv">; class RVVConvBuiltin : RVVBuiltin { let IntrinsicTypes = [-1, 0]; let OverloadedName = overloaded_name; } class RVVConvToSignedBuiltin : RVVConvBuiltin<"Iv", "Ivv", "xfd", overloaded_name>; class RVVConvToUnsignedBuiltin : RVVConvBuiltin<"Uv", "Uvv", "xfd", overloaded_name>; class RVVConvToWidenSignedBuiltin : RVVConvBuiltin<"Iw", "Iwv", "xf", overloaded_name>; class RVVConvToWidenUnsignedBuiltin : RVVConvBuiltin<"Uw", "Uwv", "xf", overloaded_name>; class RVVConvToNarrowingSignedBuiltin : RVVConvBuiltin<"Iv", "IvFw", "csi", overloaded_name>; class RVVConvToNarrowingUnsignedBuiltin : RVVConvBuiltin<"Uv", "UvFw", "csi", overloaded_name>; let HasMaskedOffOperand = true in { multiclass RVVSignedReductionBuiltin { defm "" : RVVOutOp0BuiltinSet; } multiclass RVVUnsignedReductionBuiltin { defm "" : RVVOutOp0BuiltinSet; } multiclass RVVFloatingReductionBuiltin { defm "" : RVVOutOp0BuiltinSet; } multiclass RVVFloatingReductionBuiltinRoundingMode { defm "" : RVVOutOp0BuiltinSet; } multiclass RVVFloatingWidenReductionBuiltin { defm "" : RVVOutOp0BuiltinSet; } multiclass RVVFloatingWidenReductionBuiltinRoundingMode { defm "" : RVVOutOp0BuiltinSet; } } multiclass RVVIntReductionBuiltinSet : RVVSignedReductionBuiltin, RVVUnsignedReductionBuiltin; // For widen operation which has different mangling name. multiclass RVVWidenBuiltinSet> suffixes_prototypes> { let Log2LMUL = [-3, -2, -1, 0, 1, 2], IRName = intrinsic_name, MaskedIRName = intrinsic_name # "_mask" in { foreach s_p = suffixes_prototypes in { let Name = NAME # "_" # s_p[0], OverloadedName = NAME # "_" # s_p[0] in { defvar suffix = s_p[1]; defvar prototype = s_p[2]; def : RVVOutOp0Op1Builtin; } } } } // For widen operation with widen operand which has different mangling name. multiclass RVVWidenWOp0BuiltinSet> suffixes_prototypes> { let Log2LMUL = [-3, -2, -1, 0, 1, 2], IRName = intrinsic_name, MaskedIRName = intrinsic_name # "_mask" in { foreach s_p = suffixes_prototypes in { let Name = NAME # "_" # s_p[0], OverloadedName = NAME # "_" # s_p[0] in { defvar suffix = s_p[1]; defvar prototype = s_p[2]; def : RVVOutOp1Builtin; } } } } multiclass RVVSignedWidenBinBuiltinSet : RVVWidenBuiltinSet; multiclass RVVSignedWidenOp0BinBuiltinSet : RVVWidenWOp0BuiltinSet; multiclass RVVUnsignedWidenBinBuiltinSet : RVVWidenBuiltinSet; multiclass RVVUnsignedWidenOp0BinBuiltinSet : RVVWidenWOp0BuiltinSet; multiclass RVVFloatingWidenBinBuiltinSet : RVVWidenBuiltinSet; multiclass RVVFloatingWidenBinBuiltinSetRoundingMode : RVVWidenBuiltinSet; multiclass RVVFloatingWidenOp0BinBuiltinSet : RVVWidenWOp0BuiltinSet; multiclass RVVFloatingWidenOp0BinBuiltinSetRoundingMode : RVVWidenWOp0BuiltinSet;