RISCVVIntrinsicUtils.h 16 KB

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