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/SmallVector.h"
15 #include "llvm/ADT/StringRef.h"
16 #include <cstdint>
17 #include <optional>
18 #include <set>
19 #include <string>
20 #include <unordered_map>
21 #include <vector>
22 
23 namespace llvm {
24 class raw_ostream;
25 } // end namespace llvm
26 
27 namespace clang {
28 namespace RISCV {
29 
30 using VScaleVal = std::optional<unsigned>;
31 
32 // Modifier for vector type.
33 enum class VectorTypeModifier : uint8_t {
34   NoModifier,
35   Widening2XVector,
36   Widening4XVector,
37   Widening8XVector,
38   MaskVector,
39   Log2EEW3,
40   Log2EEW4,
41   Log2EEW5,
42   Log2EEW6,
43   FixedSEW8,
44   FixedSEW16,
45   FixedSEW32,
46   FixedSEW64,
47   LFixedLog2LMULN3,
48   LFixedLog2LMULN2,
49   LFixedLog2LMULN1,
50   LFixedLog2LMUL0,
51   LFixedLog2LMUL1,
52   LFixedLog2LMUL2,
53   LFixedLog2LMUL3,
54   SFixedLog2LMULN3,
55   SFixedLog2LMULN2,
56   SFixedLog2LMULN1,
57   SFixedLog2LMUL0,
58   SFixedLog2LMUL1,
59   SFixedLog2LMUL2,
60   SFixedLog2LMUL3,
61 };
62 
63 // Similar to basic type but used to describe what's kind of type related to
64 // basic vector type, used to compute type info of arguments.
65 enum class BaseTypeModifier : uint8_t {
66   Invalid,
67   Scalar,
68   Vector,
69   Void,
70   SizeT,
71   Ptrdiff,
72   UnsignedLong,
73   SignedLong,
74 };
75 
76 // Modifier for type, used for both scalar and vector types.
77 enum class TypeModifier : uint8_t {
78   NoModifier = 0,
79   Pointer = 1 << 0,
80   Const = 1 << 1,
81   Immediate = 1 << 2,
82   UnsignedInteger = 1 << 3,
83   SignedInteger = 1 << 4,
84   Float = 1 << 5,
85   // LMUL1 should be kind of VectorTypeModifier, but that might come with
86   // Widening2XVector for widening reduction.
87   // However that might require VectorTypeModifier become bitmask rather than
88   // simple enum, so we decide keek LMUL1 in TypeModifier for code size
89   // optimization of clang binary size.
90   LMUL1 = 1 << 6,
91   MaxOffset = 6,
92   LLVM_MARK_AS_BITMASK_ENUM(LMUL1),
93 };
94 
95 class Policy {
96 public:
97   enum PolicyType {
98     Undisturbed,
99     Agnostic,
100   };
101 
102 private:
103   // The default assumption for an RVV instruction is TAMA, as an undisturbed
104   // policy generally will affect the performance of an out-of-order core.
105   const PolicyType TailPolicy = Agnostic;
106   const PolicyType MaskPolicy = Agnostic;
107 
108 public:
109   Policy() = default;
Policy(PolicyType TailPolicy)110   Policy(PolicyType TailPolicy) : TailPolicy(TailPolicy) {}
Policy(PolicyType TailPolicy,PolicyType MaskPolicy)111   Policy(PolicyType TailPolicy, PolicyType MaskPolicy)
112       : TailPolicy(TailPolicy), MaskPolicy(MaskPolicy) {}
113 
isTAMAPolicy()114   bool isTAMAPolicy() const {
115     return TailPolicy == Agnostic && MaskPolicy == Agnostic;
116   }
117 
isTAMUPolicy()118   bool isTAMUPolicy() const {
119     return TailPolicy == Agnostic && MaskPolicy == Undisturbed;
120   }
121 
isTUMAPolicy()122   bool isTUMAPolicy() const {
123     return TailPolicy == Undisturbed && MaskPolicy == Agnostic;
124   }
125 
isTUMUPolicy()126   bool isTUMUPolicy() const {
127     return TailPolicy == Undisturbed && MaskPolicy == Undisturbed;
128   }
129 
isTAPolicy()130   bool isTAPolicy() const { return TailPolicy == Agnostic; }
131 
isTUPolicy()132   bool isTUPolicy() const { return TailPolicy == Undisturbed; }
133 
isMAPolicy()134   bool isMAPolicy() const { return MaskPolicy == Agnostic; }
135 
isMUPolicy()136   bool isMUPolicy() const { return MaskPolicy == Undisturbed; }
137 
138   bool operator==(const Policy &Other) const {
139     return TailPolicy == Other.TailPolicy && MaskPolicy == Other.MaskPolicy;
140   }
141 
142   bool operator!=(const Policy &Other) const { return !(*this == Other); }
143 
144   bool operator<(const Policy &Other) const {
145     // Just for maintain the old order for quick test.
146     if (MaskPolicy != Other.MaskPolicy)
147       return Other.MaskPolicy < MaskPolicy;
148     return TailPolicy < Other.TailPolicy;
149   }
150 };
151 
152 // PrototypeDescriptor is used to compute type info of arguments or return
153 // value.
154 struct PrototypeDescriptor {
155   constexpr PrototypeDescriptor() = default;
156   constexpr PrototypeDescriptor(
157       BaseTypeModifier PT,
158       VectorTypeModifier VTM = VectorTypeModifier::NoModifier,
159       TypeModifier TM = TypeModifier::NoModifier)
PTPrototypeDescriptor160       : PT(static_cast<uint8_t>(PT)), VTM(static_cast<uint8_t>(VTM)),
161         TM(static_cast<uint8_t>(TM)) {}
PrototypeDescriptorPrototypeDescriptor162   constexpr PrototypeDescriptor(uint8_t PT, uint8_t VTM, uint8_t TM)
163       : PT(PT), VTM(VTM), TM(TM) {}
164 
165   uint8_t PT = static_cast<uint8_t>(BaseTypeModifier::Invalid);
166   uint8_t VTM = static_cast<uint8_t>(VectorTypeModifier::NoModifier);
167   uint8_t TM = static_cast<uint8_t>(TypeModifier::NoModifier);
168 
169   bool operator!=(const PrototypeDescriptor &PD) const {
170     return !(*this == PD);
171   }
172   bool operator==(const PrototypeDescriptor &PD) const {
173     return PD.PT == PT && PD.VTM == VTM && PD.TM == TM;
174   }
175   bool operator<(const PrototypeDescriptor &PD) const {
176     return std::tie(PT, VTM, TM) < std::tie(PD.PT, PD.VTM, PD.TM);
177   }
178   static const PrototypeDescriptor Mask;
179   static const PrototypeDescriptor Vector;
180   static const PrototypeDescriptor VL;
181   static std::optional<PrototypeDescriptor>
182   parsePrototypeDescriptor(llvm::StringRef PrototypeStr);
183 };
184 
185 llvm::SmallVector<PrototypeDescriptor>
186 parsePrototypes(llvm::StringRef Prototypes);
187 
188 // Basic type of vector type.
189 enum class BasicType : uint8_t {
190   Unknown = 0,
191   Int8 = 1 << 0,
192   Int16 = 1 << 1,
193   Int32 = 1 << 2,
194   Int64 = 1 << 3,
195   Float16 = 1 << 4,
196   Float32 = 1 << 5,
197   Float64 = 1 << 6,
198   MaxOffset = 6,
199   LLVM_MARK_AS_BITMASK_ENUM(Float64),
200 };
201 
202 // Type of vector type.
203 enum ScalarTypeKind : uint8_t {
204   Void,
205   Size_t,
206   Ptrdiff_t,
207   UnsignedLong,
208   SignedLong,
209   Boolean,
210   SignedInteger,
211   UnsignedInteger,
212   Float,
213   Invalid,
214 };
215 
216 // Exponential LMUL
217 struct LMULType {
218   int Log2LMUL;
219   LMULType(int Log2LMUL);
220   // Return the C/C++ string representation of LMUL
221   std::string str() const;
222   std::optional<unsigned> getScale(unsigned ElementBitwidth) const;
223   void MulLog2LMUL(int Log2LMUL);
224 };
225 
226 class RVVType;
227 using RVVTypePtr = RVVType *;
228 using RVVTypes = std::vector<RVVTypePtr>;
229 class RVVTypeCache;
230 
231 // This class is compact representation of a valid and invalid RVVType.
232 class RVVType {
233   friend class RVVTypeCache;
234 
235   BasicType BT;
236   ScalarTypeKind ScalarType = Invalid;
237   LMULType LMUL;
238   bool IsPointer = false;
239   // IsConstant indices are "int", but have the constant expression.
240   bool IsImmediate = false;
241   // Const qualifier for pointer to const object or object of const type.
242   bool IsConstant = false;
243   unsigned ElementBitwidth = 0;
244   VScaleVal Scale = 0;
245   bool Valid;
246 
247   std::string BuiltinStr;
248   std::string ClangBuiltinStr;
249   std::string Str;
250   std::string ShortStr;
251 
252   enum class FixedLMULType { LargerThan, SmallerThan };
253 
254   RVVType(BasicType BT, int Log2LMUL, const PrototypeDescriptor &Profile);
255 
256 public:
257   // Return the string representation of a type, which is an encoded string for
258   // passing to the BUILTIN() macro in Builtins.def.
getBuiltinStr()259   const std::string &getBuiltinStr() const { return BuiltinStr; }
260 
261   // Return the clang builtin type for RVV vector type which are used in the
262   // riscv_vector.h header file.
getClangBuiltinStr()263   const std::string &getClangBuiltinStr() const { return ClangBuiltinStr; }
264 
265   // Return the C/C++ string representation of a type for use in the
266   // riscv_vector.h header file.
getTypeStr()267   const std::string &getTypeStr() const { return Str; }
268 
269   // Return the short name of a type for C/C++ name suffix.
getShortStr()270   const std::string &getShortStr() {
271     // Not all types are used in short name, so compute the short name by
272     // demanded.
273     if (ShortStr.empty())
274       initShortStr();
275     return ShortStr;
276   }
277 
isValid()278   bool isValid() const { return Valid; }
isScalar()279   bool isScalar() const { return Scale && *Scale == 0; }
isVector()280   bool isVector() const { return Scale && *Scale != 0; }
isVector(unsigned Width)281   bool isVector(unsigned Width) const {
282     return isVector() && ElementBitwidth == Width;
283   }
isFloat()284   bool isFloat() const { return ScalarType == ScalarTypeKind::Float; }
isSignedInteger()285   bool isSignedInteger() const {
286     return ScalarType == ScalarTypeKind::SignedInteger;
287   }
isFloatVector(unsigned Width)288   bool isFloatVector(unsigned Width) const {
289     return isVector() && isFloat() && ElementBitwidth == Width;
290   }
isFloat(unsigned Width)291   bool isFloat(unsigned Width) const {
292     return isFloat() && ElementBitwidth == Width;
293   }
isConstant()294   bool isConstant() const { return IsConstant; }
isPointer()295   bool isPointer() const { return IsPointer; }
getElementBitwidth()296   unsigned getElementBitwidth() const { return ElementBitwidth; }
297 
getScalarType()298   ScalarTypeKind getScalarType() const { return ScalarType; }
getScale()299   VScaleVal getScale() const { return Scale; }
300 
301 private:
302   // Verify RVV vector type and set Valid.
303   bool verifyType() const;
304 
305   // Creates a type based on basic types of TypeRange
306   void applyBasicType();
307 
308   // Applies a prototype modifier to the current type. The result maybe an
309   // invalid type.
310   void applyModifier(const PrototypeDescriptor &prototype);
311 
312   void applyLog2EEW(unsigned Log2EEW);
313   void applyFixedSEW(unsigned NewSEW);
314   void applyFixedLog2LMUL(int Log2LMUL, enum FixedLMULType Type);
315 
316   // Compute and record a string for legal type.
317   void initBuiltinStr();
318   // Compute and record a builtin RVV vector type string.
319   void initClangBuiltinStr();
320   // Compute and record a type string for used in the header.
321   void initTypeStr();
322   // Compute and record a short name of a type for C/C++ name suffix.
323   void initShortStr();
324 };
325 
326 // This class is used to manage RVVType, RVVType should only created by this
327 // class, also provided thread-safe cache capability.
328 class RVVTypeCache {
329 private:
330   std::unordered_map<uint64_t, RVVType> LegalTypes;
331   std::set<uint64_t> IllegalTypes;
332 
333 public:
334   /// Compute output and input types by applying different config (basic type
335   /// and LMUL with type transformers). It also record result of type in legal
336   /// or illegal set to avoid compute the same config again. The result maybe
337   /// have illegal RVVType.
338   std::optional<RVVTypes>
339   computeTypes(BasicType BT, int Log2LMUL, unsigned NF,
340                llvm::ArrayRef<PrototypeDescriptor> Prototype);
341   std::optional<RVVTypePtr> computeType(BasicType BT, int Log2LMUL,
342                                         PrototypeDescriptor Proto);
343 };
344 
345 enum PolicyScheme : uint8_t {
346   SchemeNone,
347   // Passthru operand is at first parameter in C builtin.
348   HasPassthruOperand,
349   HasPolicyOperand,
350 };
351 
352 // TODO refactor RVVIntrinsic class design after support all intrinsic
353 // combination. This represents an instantiation of an intrinsic with a
354 // particular type and prototype
355 class RVVIntrinsic {
356 
357 private:
358   std::string BuiltinName; // Builtin name
359   std::string Name;        // C intrinsic name.
360   std::string OverloadedName;
361   std::string IRName;
362   bool IsMasked;
363   bool HasMaskedOffOperand;
364   bool HasVL;
365   PolicyScheme Scheme;
366   bool SupportOverloading;
367   bool HasBuiltinAlias;
368   std::string ManualCodegen;
369   RVVTypePtr OutputType; // Builtin output type
370   RVVTypes InputTypes;   // Builtin input types
371   // The types we use to obtain the specific LLVM intrinsic. They are index of
372   // InputTypes. -1 means the return type.
373   std::vector<int64_t> IntrinsicTypes;
374   unsigned NF = 1;
375   Policy PolicyAttrs;
376 
377 public:
378   RVVIntrinsic(llvm::StringRef Name, llvm::StringRef Suffix,
379                llvm::StringRef OverloadedName, llvm::StringRef OverloadedSuffix,
380                llvm::StringRef IRName, bool IsMasked, bool HasMaskedOffOperand,
381                bool HasVL, PolicyScheme Scheme, bool SupportOverloading,
382                bool HasBuiltinAlias, llvm::StringRef ManualCodegen,
383                const RVVTypes &Types,
384                const std::vector<int64_t> &IntrinsicTypes,
385                const std::vector<llvm::StringRef> &RequiredFeatures,
386                unsigned NF, Policy PolicyAttrs);
387   ~RVVIntrinsic() = default;
388 
getOutputType()389   RVVTypePtr getOutputType() const { return OutputType; }
getInputTypes()390   const RVVTypes &getInputTypes() const { return InputTypes; }
getBuiltinName()391   llvm::StringRef getBuiltinName() const { return BuiltinName; }
getName()392   llvm::StringRef getName() const { return Name; }
getOverloadedName()393   llvm::StringRef getOverloadedName() const { return OverloadedName; }
hasMaskedOffOperand()394   bool hasMaskedOffOperand() const { return HasMaskedOffOperand; }
hasVL()395   bool hasVL() const { return HasVL; }
hasPolicy()396   bool hasPolicy() const { return Scheme != PolicyScheme::SchemeNone; }
hasPassthruOperand()397   bool hasPassthruOperand() const {
398     return Scheme == PolicyScheme::HasPassthruOperand;
399   }
hasPolicyOperand()400   bool hasPolicyOperand() const {
401     return Scheme == PolicyScheme::HasPolicyOperand;
402   }
supportOverloading()403   bool supportOverloading() const { return SupportOverloading; }
hasBuiltinAlias()404   bool hasBuiltinAlias() const { return HasBuiltinAlias; }
hasManualCodegen()405   bool hasManualCodegen() const { return !ManualCodegen.empty(); }
isMasked()406   bool isMasked() const { return IsMasked; }
getIRName()407   llvm::StringRef getIRName() const { return IRName; }
getManualCodegen()408   llvm::StringRef getManualCodegen() const { return ManualCodegen; }
getPolicyScheme()409   PolicyScheme getPolicyScheme() const { return Scheme; }
getNF()410   unsigned getNF() const { return NF; }
getIntrinsicTypes()411   const std::vector<int64_t> &getIntrinsicTypes() const {
412     return IntrinsicTypes;
413   }
getPolicyAttrs()414   Policy getPolicyAttrs() const {
415     return PolicyAttrs;
416   }
getPolicyAttrsBits()417   unsigned getPolicyAttrsBits() const {
418     // CGBuiltin.cpp
419     // The 0th bit simulates the `vta` of RVV
420     // The 1st bit simulates the `vma` of RVV
421     // int PolicyAttrs = 0;
422 
423     if (PolicyAttrs.isTUMAPolicy())
424       return 2;
425     if (PolicyAttrs.isTAMAPolicy())
426       return 3;
427     if (PolicyAttrs.isTUMUPolicy())
428       return 0;
429     if (PolicyAttrs.isTAMUPolicy())
430       return 1;
431 
432     llvm_unreachable("unsupport policy");
433     return 0;
434   }
435 
436   // Return the type string for a BUILTIN() macro in Builtins.def.
437   std::string getBuiltinTypeStr() const;
438 
439   static std::string
440   getSuffixStr(RVVTypeCache &TypeCache, BasicType Type, int Log2LMUL,
441                llvm::ArrayRef<PrototypeDescriptor> PrototypeDescriptors);
442 
443   static llvm::SmallVector<PrototypeDescriptor>
444   computeBuiltinTypes(llvm::ArrayRef<PrototypeDescriptor> Prototype,
445                       bool IsMasked, bool HasMaskedOffOperand, bool HasVL,
446                       unsigned NF, PolicyScheme DefaultScheme,
447                       Policy PolicyAttrs);
448 
449   static llvm::SmallVector<Policy> getSupportedUnMaskedPolicies();
450   static llvm::SmallVector<Policy>
451       getSupportedMaskedPolicies(bool HasTailPolicy, bool HasMaskPolicy);
452 
453   static void updateNamesAndPolicy(bool IsMasked, bool HasPolicy,
454                                    std::string &Name, std::string &BuiltinName,
455                                    std::string &OverloadedName,
456                                    Policy &PolicyAttrs);
457 };
458 
459 // RVVRequire should be sync'ed with target features, but only
460 // required features used in riscv_vector.td.
461 enum RVVRequire : uint8_t {
462   RVV_REQ_None = 0,
463   RVV_REQ_RV64 = 1 << 0,
464   RVV_REQ_FullMultiply = 1 << 1,
465 
466   LLVM_MARK_AS_BITMASK_ENUM(RVV_REQ_FullMultiply)
467 };
468 
469 // Raw RVV intrinsic info, used to expand later.
470 // This struct is highly compact for minimized code size.
471 struct RVVIntrinsicRecord {
472   // Intrinsic name, e.g. vadd_vv
473   const char *Name;
474 
475   // Overloaded intrinsic name, could be empty if it can be computed from Name.
476   // e.g. vadd
477   const char *OverloadedName;
478 
479   // Prototype for this intrinsic, index of RVVSignatureTable.
480   uint16_t PrototypeIndex;
481 
482   // Suffix of intrinsic name, index of RVVSignatureTable.
483   uint16_t SuffixIndex;
484 
485   // Suffix of overloaded intrinsic name, index of RVVSignatureTable.
486   uint16_t OverloadedSuffixIndex;
487 
488   // Length of the prototype.
489   uint8_t PrototypeLength;
490 
491   // Length of intrinsic name suffix.
492   uint8_t SuffixLength;
493 
494   // Length of overloaded intrinsic suffix.
495   uint8_t OverloadedSuffixSize;
496 
497   // Required target features for this intrinsic.
498   uint8_t RequiredExtensions;
499 
500   // Supported type, mask of BasicType.
501   uint8_t TypeRangeMask;
502 
503   // Supported LMUL.
504   uint8_t Log2LMULMask;
505 
506   // Number of fields, greater than 1 if it's segment load/store.
507   uint8_t NF;
508 
509   bool HasMasked : 1;
510   bool HasVL : 1;
511   bool HasMaskedOffOperand : 1;
512   bool HasTailPolicy : 1;
513   bool HasMaskPolicy : 1;
514   uint8_t UnMaskedPolicyScheme : 2;
515   uint8_t MaskedPolicyScheme : 2;
516 };
517 
518 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
519                               const RVVIntrinsicRecord &RVVInstrRecord);
520 
521 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
522 } // end namespace RISCV
523 
524 } // end namespace clang
525 
526 #endif // CLANG_SUPPORT_RISCVVINTRINSICUTILS_H
527