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