1 //===--- RISCVVIntrinsicUtils.h - RISC-V Vector Intrinsic Utils -*- 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 
9 #ifndef CLANG_SUPPORT_RISCVVINTRINSICUTILS_H
10 #define CLANG_SUPPORT_RISCVVINTRINSICUTILS_H
11 
12 #include "llvm/ADT/ArrayRef.h"
13 #include "llvm/ADT/BitmaskEnum.h"
14 #include "llvm/ADT/Optional.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringRef.h"
17 #include <cstdint>
18 #include <string>
19 #include <vector>
20 
21 namespace llvm {
22 class raw_ostream;
23 } // end namespace llvm
24 
25 namespace clang {
26 namespace RISCV {
27 
28 using VScaleVal = llvm::Optional<unsigned>;
29 
30 // Modifier for vector type.
31 enum class VectorTypeModifier : uint8_t {
32   NoModifier,
33   Widening2XVector,
34   Widening4XVector,
35   Widening8XVector,
36   MaskVector,
37   Log2EEW3,
38   Log2EEW4,
39   Log2EEW5,
40   Log2EEW6,
41   FixedSEW8,
42   FixedSEW16,
43   FixedSEW32,
44   FixedSEW64,
45   LFixedLog2LMULN3,
46   LFixedLog2LMULN2,
47   LFixedLog2LMULN1,
48   LFixedLog2LMUL0,
49   LFixedLog2LMUL1,
50   LFixedLog2LMUL2,
51   LFixedLog2LMUL3,
52   SFixedLog2LMULN3,
53   SFixedLog2LMULN2,
54   SFixedLog2LMULN1,
55   SFixedLog2LMUL0,
56   SFixedLog2LMUL1,
57   SFixedLog2LMUL2,
58   SFixedLog2LMUL3,
59 };
60 
61 // Similar to basic type but used to describe what's kind of type related to
62 // basic vector type, used to compute type info of arguments.
63 enum class BaseTypeModifier : uint8_t {
64   Invalid,
65   Scalar,
66   Vector,
67   Void,
68   SizeT,
69   Ptrdiff,
70   UnsignedLong,
71   SignedLong,
72 };
73 
74 // Modifier for type, used for both scalar and vector types.
75 enum class TypeModifier : uint8_t {
76   NoModifier = 0,
77   Pointer = 1 << 0,
78   Const = 1 << 1,
79   Immediate = 1 << 2,
80   UnsignedInteger = 1 << 3,
81   SignedInteger = 1 << 4,
82   Float = 1 << 5,
83   // LMUL1 should be kind of VectorTypeModifier, but that might come with
84   // Widening2XVector for widening reduction.
85   // However that might require VectorTypeModifier become bitmask rather than
86   // simple enum, so we decide keek LMUL1 in TypeModifier for code size
87   // optimization of clang binary size.
88   LMUL1 = 1 << 6,
89   MaxOffset = 6,
90   LLVM_MARK_AS_BITMASK_ENUM(LMUL1),
91 };
92 
93 // PrototypeDescriptor is used to compute type info of arguments or return
94 // value.
95 struct PrototypeDescriptor {
96   constexpr PrototypeDescriptor() = default;
97   constexpr PrototypeDescriptor(
98       BaseTypeModifier PT,
99       VectorTypeModifier VTM = VectorTypeModifier::NoModifier,
100       TypeModifier TM = TypeModifier::NoModifier)
101       : PT(static_cast<uint8_t>(PT)), VTM(static_cast<uint8_t>(VTM)),
102         TM(static_cast<uint8_t>(TM)) {}
103   constexpr PrototypeDescriptor(uint8_t PT, uint8_t VTM, uint8_t TM)
104       : PT(PT), VTM(VTM), TM(TM) {}
105 
106   uint8_t PT = static_cast<uint8_t>(BaseTypeModifier::Invalid);
107   uint8_t VTM = static_cast<uint8_t>(VectorTypeModifier::NoModifier);
108   uint8_t TM = static_cast<uint8_t>(TypeModifier::NoModifier);
109 
110   bool operator!=(const PrototypeDescriptor &PD) const {
111     return !(*this == PD);
112   }
113   bool operator==(const PrototypeDescriptor &PD) const {
114     return PD.PT == PT && PD.VTM == VTM && PD.TM == TM;
115   }
116   bool operator<(const PrototypeDescriptor &PD) const {
117     return std::tie(PT, VTM, TM) < std::tie(PD.PT, PD.VTM, PD.TM);
118   }
119   static const PrototypeDescriptor Mask;
120   static const PrototypeDescriptor Vector;
121   static const PrototypeDescriptor VL;
122   static llvm::Optional<PrototypeDescriptor>
123   parsePrototypeDescriptor(llvm::StringRef PrototypeStr);
124 };
125 
126 llvm::SmallVector<PrototypeDescriptor>
127 parsePrototypes(llvm::StringRef Prototypes);
128 
129 // Basic type of vector type.
130 enum class BasicType : uint8_t {
131   Unknown = 0,
132   Int8 = 1 << 0,
133   Int16 = 1 << 1,
134   Int32 = 1 << 2,
135   Int64 = 1 << 3,
136   Float16 = 1 << 4,
137   Float32 = 1 << 5,
138   Float64 = 1 << 6,
139   MaxOffset = 6,
140   LLVM_MARK_AS_BITMASK_ENUM(Float64),
141 };
142 
143 // Type of vector type.
144 enum ScalarTypeKind : uint8_t {
145   Void,
146   Size_t,
147   Ptrdiff_t,
148   UnsignedLong,
149   SignedLong,
150   Boolean,
151   SignedInteger,
152   UnsignedInteger,
153   Float,
154   Invalid,
155 };
156 
157 // Exponential LMUL
158 struct LMULType {
159   int Log2LMUL;
160   LMULType(int Log2LMUL);
161   // Return the C/C++ string representation of LMUL
162   std::string str() const;
163   llvm::Optional<unsigned> getScale(unsigned ElementBitwidth) const;
164   void MulLog2LMUL(int Log2LMUL);
165 };
166 
167 class RVVType;
168 using RVVTypePtr = RVVType *;
169 using RVVTypes = std::vector<RVVTypePtr>;
170 
171 // This class is compact representation of a valid and invalid RVVType.
172 class RVVType {
173   BasicType BT;
174   ScalarTypeKind ScalarType = Invalid;
175   LMULType LMUL;
176   bool IsPointer = false;
177   // IsConstant indices are "int", but have the constant expression.
178   bool IsImmediate = false;
179   // Const qualifier for pointer to const object or object of const type.
180   bool IsConstant = false;
181   unsigned ElementBitwidth = 0;
182   VScaleVal Scale = 0;
183   bool Valid;
184 
185   std::string BuiltinStr;
186   std::string ClangBuiltinStr;
187   std::string Str;
188   std::string ShortStr;
189 
190   enum class FixedLMULType { LargerThan, SmallerThan };
191 
192 public:
193   RVVType() : BT(BasicType::Unknown), LMUL(0), Valid(false) {}
194   RVVType(BasicType BT, int Log2LMUL, const PrototypeDescriptor &Profile);
195 
196   // Return the string representation of a type, which is an encoded string for
197   // passing to the BUILTIN() macro in Builtins.def.
198   const std::string &getBuiltinStr() const { return BuiltinStr; }
199 
200   // Return the clang builtin type for RVV vector type which are used in the
201   // riscv_vector.h header file.
202   const std::string &getClangBuiltinStr() const { return ClangBuiltinStr; }
203 
204   // Return the C/C++ string representation of a type for use in the
205   // riscv_vector.h header file.
206   const std::string &getTypeStr() const { return Str; }
207 
208   // Return the short name of a type for C/C++ name suffix.
209   const std::string &getShortStr() {
210     // Not all types are used in short name, so compute the short name by
211     // demanded.
212     if (ShortStr.empty())
213       initShortStr();
214     return ShortStr;
215   }
216 
217   bool isValid() const { return Valid; }
218   bool isScalar() const { return Scale && Scale.value() == 0; }
219   bool isVector() const { return Scale && Scale.value() != 0; }
220   bool isVector(unsigned Width) const {
221     return isVector() && ElementBitwidth == Width;
222   }
223   bool isFloat() const { return ScalarType == ScalarTypeKind::Float; }
224   bool isSignedInteger() const {
225     return ScalarType == ScalarTypeKind::SignedInteger;
226   }
227   bool isFloatVector(unsigned Width) const {
228     return isVector() && isFloat() && ElementBitwidth == Width;
229   }
230   bool isFloat(unsigned Width) const {
231     return isFloat() && ElementBitwidth == Width;
232   }
233   bool isConstant() const { return IsConstant; }
234   bool isPointer() const { return IsPointer; }
235   unsigned getElementBitwidth() const { return ElementBitwidth; }
236 
237   ScalarTypeKind getScalarType() const { return ScalarType; }
238   VScaleVal getScale() const { return Scale; }
239 
240 private:
241   // Verify RVV vector type and set Valid.
242   bool verifyType() const;
243 
244   // Creates a type based on basic types of TypeRange
245   void applyBasicType();
246 
247   // Applies a prototype modifier to the current type. The result maybe an
248   // invalid type.
249   void applyModifier(const PrototypeDescriptor &prototype);
250 
251   void applyLog2EEW(unsigned Log2EEW);
252   void applyFixedSEW(unsigned NewSEW);
253   void applyFixedLog2LMUL(int Log2LMUL, enum FixedLMULType Type);
254 
255   // Compute and record a string for legal type.
256   void initBuiltinStr();
257   // Compute and record a builtin RVV vector type string.
258   void initClangBuiltinStr();
259   // Compute and record a type string for used in the header.
260   void initTypeStr();
261   // Compute and record a short name of a type for C/C++ name suffix.
262   void initShortStr();
263 
264 public:
265   /// Compute output and input types by applying different config (basic type
266   /// and LMUL with type transformers). It also record result of type in legal
267   /// or illegal set to avoid compute the same config again. The result maybe
268   /// have illegal RVVType.
269   static llvm::Optional<RVVTypes>
270   computeTypes(BasicType BT, int Log2LMUL, unsigned NF,
271                llvm::ArrayRef<PrototypeDescriptor> Prototype);
272   static llvm::Optional<RVVTypePtr> computeType(BasicType BT, int Log2LMUL,
273                                                 PrototypeDescriptor Proto);
274 };
275 
276 enum PolicyScheme : uint8_t {
277   SchemeNone,
278   HasPassthruOperand,
279   HasPolicyOperand,
280 };
281 
282 // TODO refactor RVVIntrinsic class design after support all intrinsic
283 // combination. This represents an instantiation of an intrinsic with a
284 // particular type and prototype
285 class RVVIntrinsic {
286 
287 private:
288   std::string BuiltinName; // Builtin name
289   std::string Name;        // C intrinsic name.
290   std::string OverloadedName;
291   std::string IRName;
292   bool IsMasked;
293   bool HasVL;
294   PolicyScheme Scheme;
295   bool HasUnMaskedOverloaded;
296   bool HasBuiltinAlias;
297   std::string ManualCodegen;
298   RVVTypePtr OutputType; // Builtin output type
299   RVVTypes InputTypes;   // Builtin input types
300   // The types we use to obtain the specific LLVM intrinsic. They are index of
301   // InputTypes. -1 means the return type.
302   std::vector<int64_t> IntrinsicTypes;
303   unsigned NF = 1;
304 
305 public:
306   RVVIntrinsic(llvm::StringRef Name, llvm::StringRef Suffix,
307                llvm::StringRef OverloadedName, llvm::StringRef OverloadedSuffix,
308                llvm::StringRef IRName, bool IsMasked, bool HasMaskedOffOperand,
309                bool HasVL, PolicyScheme Scheme, bool HasUnMaskedOverloaded,
310                bool HasBuiltinAlias, llvm::StringRef ManualCodegen,
311                const RVVTypes &Types,
312                const std::vector<int64_t> &IntrinsicTypes,
313                const std::vector<llvm::StringRef> &RequiredFeatures,
314                unsigned NF);
315   ~RVVIntrinsic() = default;
316 
317   RVVTypePtr getOutputType() const { return OutputType; }
318   const RVVTypes &getInputTypes() const { return InputTypes; }
319   llvm::StringRef getBuiltinName() const { return BuiltinName; }
320   llvm::StringRef getName() const { return Name; }
321   llvm::StringRef getOverloadedName() const { return OverloadedName; }
322   bool hasVL() const { return HasVL; }
323   bool hasPolicy() const { return Scheme != SchemeNone; }
324   bool hasPassthruOperand() const { return Scheme == HasPassthruOperand; }
325   bool hasPolicyOperand() const { return Scheme == HasPolicyOperand; }
326   bool hasUnMaskedOverloaded() const { return HasUnMaskedOverloaded; }
327   bool hasBuiltinAlias() const { return HasBuiltinAlias; }
328   bool hasManualCodegen() const { return !ManualCodegen.empty(); }
329   bool isMasked() const { return IsMasked; }
330   llvm::StringRef getIRName() const { return IRName; }
331   llvm::StringRef getManualCodegen() const { return ManualCodegen; }
332   PolicyScheme getPolicyScheme() const { return Scheme; }
333   unsigned getNF() const { return NF; }
334   const std::vector<int64_t> &getIntrinsicTypes() const {
335     return IntrinsicTypes;
336   }
337 
338   // Return the type string for a BUILTIN() macro in Builtins.def.
339   std::string getBuiltinTypeStr() const;
340 
341   static std::string
342   getSuffixStr(BasicType Type, int Log2LMUL,
343                llvm::ArrayRef<PrototypeDescriptor> PrototypeDescriptors);
344 
345   static llvm::SmallVector<PrototypeDescriptor>
346       computeBuiltinTypes(llvm::ArrayRef<PrototypeDescriptor> Prototype,
347                           bool IsMasked, bool HasMaskedOffOperand, bool HasVL,
348                           unsigned NF);
349 };
350 
351 // RVVRequire should be sync'ed with target features, but only
352 // required features used in riscv_vector.td.
353 enum RVVRequire : uint8_t {
354   RVV_REQ_None = 0,
355   RVV_REQ_RV64 = 1 << 0,
356   RVV_REQ_FullMultiply = 1 << 1,
357 
358   LLVM_MARK_AS_BITMASK_ENUM(RVV_REQ_FullMultiply)
359 };
360 
361 // Raw RVV intrinsic info, used to expand later.
362 // This struct is highly compact for minimized code size.
363 struct RVVIntrinsicRecord {
364   // Intrinsic name, e.g. vadd_vv
365   const char *Name;
366 
367   // Overloaded intrinsic name, could be empty if it can be computed from Name.
368   // e.g. vadd
369   const char *OverloadedName;
370 
371   // Prototype for this intrinsic, index of RVVSignatureTable.
372   uint16_t PrototypeIndex;
373 
374   // Suffix of intrinsic name, index of RVVSignatureTable.
375   uint16_t SuffixIndex;
376 
377   // Suffix of overloaded intrinsic name, index of RVVSignatureTable.
378   uint16_t OverloadedSuffixIndex;
379 
380   // Length of the prototype.
381   uint8_t PrototypeLength;
382 
383   // Length of intrinsic name suffix.
384   uint8_t SuffixLength;
385 
386   // Length of overloaded intrinsic suffix.
387   uint8_t OverloadedSuffixSize;
388 
389   // Required target features for this intrinsic.
390   uint8_t RequiredExtensions;
391 
392   // Supported type, mask of BasicType.
393   uint8_t TypeRangeMask;
394 
395   // Supported LMUL.
396   uint8_t Log2LMULMask;
397 
398   // Number of fields, greater than 1 if it's segment load/store.
399   uint8_t NF;
400 
401   bool HasMasked : 1;
402   bool HasVL : 1;
403   bool HasMaskedOffOperand : 1;
404 };
405 
406 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
407                               const RVVIntrinsicRecord &RVVInstrRecord);
408 
409 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
410 } // end namespace RISCV
411 
412 } // end namespace clang
413 
414 #endif // CLANG_SUPPORT_RISCVVINTRINSICUTILS_H
415