1 //==- SemaRISCVVectorLookup.cpp - Name Lookup for RISC-V Vector Intrinsic -==// 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 // This file implements name lookup for RISC-V vector intrinsic. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/AST/ASTContext.h" 14 #include "clang/AST/Decl.h" 15 #include "clang/Basic/Builtins.h" 16 #include "clang/Basic/TargetInfo.h" 17 #include "clang/Lex/Preprocessor.h" 18 #include "clang/Sema/Lookup.h" 19 #include "clang/Sema/RISCVIntrinsicManager.h" 20 #include "clang/Sema/Sema.h" 21 #include "clang/Support/RISCVVIntrinsicUtils.h" 22 #include "llvm/ADT/SmallVector.h" 23 #include <optional> 24 #include <string> 25 #include <vector> 26 27 using namespace llvm; 28 using namespace clang; 29 using namespace clang::RISCV; 30 31 using IntrinsicKind = sema::RISCVIntrinsicManager::IntrinsicKind; 32 33 namespace { 34 35 // Function definition of a RVV intrinsic. 36 struct RVVIntrinsicDef { 37 /// Full function name with suffix, e.g. vadd_vv_i32m1. 38 std::string Name; 39 40 /// Overloaded function name, e.g. vadd. 41 std::string OverloadName; 42 43 /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd. 44 std::string BuiltinName; 45 46 /// Function signature, first element is return type. 47 RVVTypes Signature; 48 }; 49 50 struct RVVOverloadIntrinsicDef { 51 // Indexes of RISCVIntrinsicManagerImpl::IntrinsicList. 52 SmallVector<size_t, 8> Indexes; 53 }; 54 55 } // namespace 56 57 static const PrototypeDescriptor RVVSignatureTable[] = { 58 #define DECL_SIGNATURE_TABLE 59 #include "clang/Basic/riscv_vector_builtin_sema.inc" 60 #undef DECL_SIGNATURE_TABLE 61 }; 62 63 static const PrototypeDescriptor RVSiFiveVectorSignatureTable[] = { 64 #define DECL_SIGNATURE_TABLE 65 #include "clang/Basic/riscv_sifive_vector_builtin_sema.inc" 66 #undef DECL_SIGNATURE_TABLE 67 }; 68 69 static const RVVIntrinsicRecord RVVIntrinsicRecords[] = { 70 #define DECL_INTRINSIC_RECORDS 71 #include "clang/Basic/riscv_vector_builtin_sema.inc" 72 #undef DECL_INTRINSIC_RECORDS 73 }; 74 75 static const RVVIntrinsicRecord RVSiFiveVectorIntrinsicRecords[] = { 76 #define DECL_INTRINSIC_RECORDS 77 #include "clang/Basic/riscv_sifive_vector_builtin_sema.inc" 78 #undef DECL_INTRINSIC_RECORDS 79 }; 80 81 // Get subsequence of signature table. 82 static ArrayRef<PrototypeDescriptor> 83 ProtoSeq2ArrayRef(IntrinsicKind K, uint16_t Index, uint8_t Length) { 84 switch (K) { 85 case IntrinsicKind::RVV: 86 return ArrayRef(&RVVSignatureTable[Index], Length); 87 case IntrinsicKind::SIFIVE_VECTOR: 88 return ArrayRef(&RVSiFiveVectorSignatureTable[Index], Length); 89 } 90 llvm_unreachable("Unhandled IntrinsicKind"); 91 } 92 93 static QualType RVVType2Qual(ASTContext &Context, const RVVType *Type) { 94 QualType QT; 95 switch (Type->getScalarType()) { 96 case ScalarTypeKind::Void: 97 QT = Context.VoidTy; 98 break; 99 case ScalarTypeKind::Size_t: 100 QT = Context.getSizeType(); 101 break; 102 case ScalarTypeKind::Ptrdiff_t: 103 QT = Context.getPointerDiffType(); 104 break; 105 case ScalarTypeKind::UnsignedLong: 106 QT = Context.UnsignedLongTy; 107 break; 108 case ScalarTypeKind::SignedLong: 109 QT = Context.LongTy; 110 break; 111 case ScalarTypeKind::Boolean: 112 QT = Context.BoolTy; 113 break; 114 case ScalarTypeKind::SignedInteger: 115 QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), true); 116 break; 117 case ScalarTypeKind::UnsignedInteger: 118 QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), false); 119 break; 120 case ScalarTypeKind::Float: 121 switch (Type->getElementBitwidth()) { 122 case 64: 123 QT = Context.DoubleTy; 124 break; 125 case 32: 126 QT = Context.FloatTy; 127 break; 128 case 16: 129 QT = Context.Float16Ty; 130 break; 131 default: 132 llvm_unreachable("Unsupported floating point width."); 133 } 134 break; 135 case Invalid: 136 llvm_unreachable("Unhandled type."); 137 } 138 if (Type->isVector()) { 139 if (Type->isTuple()) 140 QT = Context.getScalableVectorType(QT, *Type->getScale(), Type->getNF()); 141 else 142 QT = Context.getScalableVectorType(QT, *Type->getScale()); 143 } 144 145 if (Type->isConstant()) 146 QT = Context.getConstType(QT); 147 148 // Transform the type to a pointer as the last step, if necessary. 149 if (Type->isPointer()) 150 QT = Context.getPointerType(QT); 151 152 return QT; 153 } 154 155 namespace { 156 class RISCVIntrinsicManagerImpl : public sema::RISCVIntrinsicManager { 157 private: 158 Sema &S; 159 ASTContext &Context; 160 RVVTypeCache TypeCache; 161 bool ConstructedRISCVVBuiltins; 162 bool ConstructedRISCVSiFiveVectorBuiltins; 163 164 // List of all RVV intrinsic. 165 std::vector<RVVIntrinsicDef> IntrinsicList; 166 // Mapping function name to index of IntrinsicList. 167 StringMap<size_t> Intrinsics; 168 // Mapping function name to RVVOverloadIntrinsicDef. 169 StringMap<RVVOverloadIntrinsicDef> OverloadIntrinsics; 170 171 172 // Create RVVIntrinsicDef. 173 void InitRVVIntrinsic(const RVVIntrinsicRecord &Record, StringRef SuffixStr, 174 StringRef OverloadedSuffixStr, bool IsMask, 175 RVVTypes &Types, bool HasPolicy, Policy PolicyAttrs); 176 177 // Create FunctionDecl for a vector intrinsic. 178 void CreateRVVIntrinsicDecl(LookupResult &LR, IdentifierInfo *II, 179 Preprocessor &PP, unsigned Index, 180 bool IsOverload); 181 182 void ConstructRVVIntrinsics(ArrayRef<RVVIntrinsicRecord> Recs, 183 IntrinsicKind K); 184 185 public: 186 RISCVIntrinsicManagerImpl(clang::Sema &S) : S(S), Context(S.Context) { 187 ConstructedRISCVVBuiltins = false; 188 ConstructedRISCVSiFiveVectorBuiltins = false; 189 } 190 191 // Initialize IntrinsicList 192 void InitIntrinsicList() override; 193 194 // Create RISC-V vector intrinsic and insert into symbol table if found, and 195 // return true, otherwise return false. 196 bool CreateIntrinsicIfFound(LookupResult &LR, IdentifierInfo *II, 197 Preprocessor &PP) override; 198 }; 199 } // namespace 200 201 void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics( 202 ArrayRef<RVVIntrinsicRecord> Recs, IntrinsicKind K) { 203 const TargetInfo &TI = Context.getTargetInfo(); 204 bool HasRV64 = TI.hasFeature("64bit"); 205 // Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics 206 // in RISCVVEmitter.cpp. 207 for (auto &Record : Recs) { 208 // Create Intrinsics for each type and LMUL. 209 BasicType BaseType = BasicType::Unknown; 210 ArrayRef<PrototypeDescriptor> BasicProtoSeq = 211 ProtoSeq2ArrayRef(K, Record.PrototypeIndex, Record.PrototypeLength); 212 ArrayRef<PrototypeDescriptor> SuffixProto = 213 ProtoSeq2ArrayRef(K, Record.SuffixIndex, Record.SuffixLength); 214 ArrayRef<PrototypeDescriptor> OverloadedSuffixProto = ProtoSeq2ArrayRef( 215 K, Record.OverloadedSuffixIndex, Record.OverloadedSuffixSize); 216 217 PolicyScheme UnMaskedPolicyScheme = 218 static_cast<PolicyScheme>(Record.UnMaskedPolicyScheme); 219 PolicyScheme MaskedPolicyScheme = 220 static_cast<PolicyScheme>(Record.MaskedPolicyScheme); 221 222 const Policy DefaultPolicy; 223 224 llvm::SmallVector<PrototypeDescriptor> ProtoSeq = 225 RVVIntrinsic::computeBuiltinTypes( 226 BasicProtoSeq, /*IsMasked=*/false, 227 /*HasMaskedOffOperand=*/false, Record.HasVL, Record.NF, 228 UnMaskedPolicyScheme, DefaultPolicy, Record.IsTuple); 229 230 llvm::SmallVector<PrototypeDescriptor> ProtoMaskSeq = 231 RVVIntrinsic::computeBuiltinTypes( 232 BasicProtoSeq, /*IsMasked=*/true, Record.HasMaskedOffOperand, 233 Record.HasVL, Record.NF, MaskedPolicyScheme, DefaultPolicy, 234 Record.IsTuple); 235 236 bool UnMaskedHasPolicy = UnMaskedPolicyScheme != PolicyScheme::SchemeNone; 237 bool MaskedHasPolicy = MaskedPolicyScheme != PolicyScheme::SchemeNone; 238 SmallVector<Policy> SupportedUnMaskedPolicies = 239 RVVIntrinsic::getSupportedUnMaskedPolicies(); 240 SmallVector<Policy> SupportedMaskedPolicies = 241 RVVIntrinsic::getSupportedMaskedPolicies(Record.HasTailPolicy, 242 Record.HasMaskPolicy); 243 244 for (unsigned int TypeRangeMaskShift = 0; 245 TypeRangeMaskShift <= static_cast<unsigned int>(BasicType::MaxOffset); 246 ++TypeRangeMaskShift) { 247 unsigned int BaseTypeI = 1 << TypeRangeMaskShift; 248 BaseType = static_cast<BasicType>(BaseTypeI); 249 250 if ((BaseTypeI & Record.TypeRangeMask) != BaseTypeI) 251 continue; 252 253 // Check requirement. 254 if (((Record.RequiredExtensions & RVV_REQ_RV64) == RVV_REQ_RV64) && 255 !HasRV64) 256 continue; 257 258 // Expanded with different LMUL. 259 for (int Log2LMUL = -3; Log2LMUL <= 3; Log2LMUL++) { 260 if (!(Record.Log2LMULMask & (1 << (Log2LMUL + 3)))) 261 continue; 262 263 std::optional<RVVTypes> Types = 264 TypeCache.computeTypes(BaseType, Log2LMUL, Record.NF, ProtoSeq); 265 266 // Ignored to create new intrinsic if there are any illegal types. 267 if (!Types.has_value()) 268 continue; 269 270 std::string SuffixStr = RVVIntrinsic::getSuffixStr( 271 TypeCache, BaseType, Log2LMUL, SuffixProto); 272 std::string OverloadedSuffixStr = RVVIntrinsic::getSuffixStr( 273 TypeCache, BaseType, Log2LMUL, OverloadedSuffixProto); 274 275 // Create non-masked intrinsic. 276 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, false, *Types, 277 UnMaskedHasPolicy, DefaultPolicy); 278 279 // Create non-masked policy intrinsic. 280 if (Record.UnMaskedPolicyScheme != PolicyScheme::SchemeNone) { 281 for (auto P : SupportedUnMaskedPolicies) { 282 llvm::SmallVector<PrototypeDescriptor> PolicyPrototype = 283 RVVIntrinsic::computeBuiltinTypes( 284 BasicProtoSeq, /*IsMasked=*/false, 285 /*HasMaskedOffOperand=*/false, Record.HasVL, Record.NF, 286 UnMaskedPolicyScheme, P, Record.IsTuple); 287 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes( 288 BaseType, Log2LMUL, Record.NF, PolicyPrototype); 289 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, 290 /*IsMask=*/false, *PolicyTypes, UnMaskedHasPolicy, 291 P); 292 } 293 } 294 if (!Record.HasMasked) 295 continue; 296 // Create masked intrinsic. 297 std::optional<RVVTypes> MaskTypes = 298 TypeCache.computeTypes(BaseType, Log2LMUL, Record.NF, ProtoMaskSeq); 299 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, true, 300 *MaskTypes, MaskedHasPolicy, DefaultPolicy); 301 if (Record.MaskedPolicyScheme == PolicyScheme::SchemeNone) 302 continue; 303 // Create masked policy intrinsic. 304 for (auto P : SupportedMaskedPolicies) { 305 llvm::SmallVector<PrototypeDescriptor> PolicyPrototype = 306 RVVIntrinsic::computeBuiltinTypes( 307 BasicProtoSeq, /*IsMasked=*/true, Record.HasMaskedOffOperand, 308 Record.HasVL, Record.NF, MaskedPolicyScheme, P, 309 Record.IsTuple); 310 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes( 311 BaseType, Log2LMUL, Record.NF, PolicyPrototype); 312 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, 313 /*IsMask=*/true, *PolicyTypes, MaskedHasPolicy, P); 314 } 315 } // End for different LMUL 316 } // End for different TypeRange 317 } 318 } 319 320 void RISCVIntrinsicManagerImpl::InitIntrinsicList() { 321 322 if (S.DeclareRISCVVBuiltins && !ConstructedRISCVVBuiltins) { 323 ConstructedRISCVVBuiltins = true; 324 ConstructRVVIntrinsics(RVVIntrinsicRecords, 325 IntrinsicKind::RVV); 326 } 327 if (S.DeclareRISCVSiFiveVectorBuiltins && 328 !ConstructedRISCVSiFiveVectorBuiltins) { 329 ConstructedRISCVSiFiveVectorBuiltins = true; 330 ConstructRVVIntrinsics(RVSiFiveVectorIntrinsicRecords, 331 IntrinsicKind::SIFIVE_VECTOR); 332 } 333 } 334 335 // Compute name and signatures for intrinsic with practical types. 336 void RISCVIntrinsicManagerImpl::InitRVVIntrinsic( 337 const RVVIntrinsicRecord &Record, StringRef SuffixStr, 338 StringRef OverloadedSuffixStr, bool IsMasked, RVVTypes &Signature, 339 bool HasPolicy, Policy PolicyAttrs) { 340 // Function name, e.g. vadd_vv_i32m1. 341 std::string Name = Record.Name; 342 if (!SuffixStr.empty()) 343 Name += "_" + SuffixStr.str(); 344 345 // Overloaded function name, e.g. vadd. 346 std::string OverloadedName; 347 if (!Record.OverloadedName) 348 OverloadedName = StringRef(Record.Name).split("_").first.str(); 349 else 350 OverloadedName = Record.OverloadedName; 351 if (!OverloadedSuffixStr.empty()) 352 OverloadedName += "_" + OverloadedSuffixStr.str(); 353 354 // clang built-in function name, e.g. __builtin_rvv_vadd. 355 std::string BuiltinName = "__builtin_rvv_" + std::string(Record.Name); 356 357 RVVIntrinsic::updateNamesAndPolicy(IsMasked, HasPolicy, Name, BuiltinName, 358 OverloadedName, PolicyAttrs, 359 Record.HasFRMRoundModeOp); 360 361 // Put into IntrinsicList. 362 size_t Index = IntrinsicList.size(); 363 IntrinsicList.push_back({Name, OverloadedName, BuiltinName, Signature}); 364 365 // Creating mapping to Intrinsics. 366 Intrinsics.insert({Name, Index}); 367 368 // Get the RVVOverloadIntrinsicDef. 369 RVVOverloadIntrinsicDef &OverloadIntrinsicDef = 370 OverloadIntrinsics[OverloadedName]; 371 372 // And added the index. 373 OverloadIntrinsicDef.Indexes.push_back(Index); 374 } 375 376 void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult &LR, 377 IdentifierInfo *II, 378 Preprocessor &PP, 379 unsigned Index, 380 bool IsOverload) { 381 ASTContext &Context = S.Context; 382 RVVIntrinsicDef &IDef = IntrinsicList[Index]; 383 RVVTypes Sigs = IDef.Signature; 384 size_t SigLength = Sigs.size(); 385 RVVType *ReturnType = Sigs[0]; 386 QualType RetType = RVVType2Qual(Context, ReturnType); 387 SmallVector<QualType, 8> ArgTypes; 388 QualType BuiltinFuncType; 389 390 // Skip return type, and convert RVVType to QualType for arguments. 391 for (size_t i = 1; i < SigLength; ++i) 392 ArgTypes.push_back(RVVType2Qual(Context, Sigs[i])); 393 394 FunctionProtoType::ExtProtoInfo PI( 395 Context.getDefaultCallingConvention(false, false, true)); 396 397 PI.Variadic = false; 398 399 SourceLocation Loc = LR.getNameLoc(); 400 BuiltinFuncType = Context.getFunctionType(RetType, ArgTypes, PI); 401 DeclContext *Parent = Context.getTranslationUnitDecl(); 402 403 FunctionDecl *RVVIntrinsicDecl = FunctionDecl::Create( 404 Context, Parent, Loc, Loc, II, BuiltinFuncType, /*TInfo=*/nullptr, 405 SC_Extern, S.getCurFPFeatures().isFPConstrained(), 406 /*isInlineSpecified*/ false, 407 /*hasWrittenPrototype*/ true); 408 409 // Create Decl objects for each parameter, adding them to the 410 // FunctionDecl. 411 const auto *FP = cast<FunctionProtoType>(BuiltinFuncType); 412 SmallVector<ParmVarDecl *, 8> ParmList; 413 for (unsigned IParm = 0, E = FP->getNumParams(); IParm != E; ++IParm) { 414 ParmVarDecl *Parm = 415 ParmVarDecl::Create(Context, RVVIntrinsicDecl, Loc, Loc, nullptr, 416 FP->getParamType(IParm), nullptr, SC_None, nullptr); 417 Parm->setScopeInfo(0, IParm); 418 ParmList.push_back(Parm); 419 } 420 RVVIntrinsicDecl->setParams(ParmList); 421 422 // Add function attributes. 423 if (IsOverload) 424 RVVIntrinsicDecl->addAttr(OverloadableAttr::CreateImplicit(Context)); 425 426 // Setup alias to __builtin_rvv_* 427 IdentifierInfo &IntrinsicII = PP.getIdentifierTable().get(IDef.BuiltinName); 428 RVVIntrinsicDecl->addAttr( 429 BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII)); 430 431 // Add to symbol table. 432 LR.addDecl(RVVIntrinsicDecl); 433 } 434 435 bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult &LR, 436 IdentifierInfo *II, 437 Preprocessor &PP) { 438 StringRef Name = II->getName(); 439 440 // Lookup the function name from the overload intrinsics first. 441 auto OvIItr = OverloadIntrinsics.find(Name); 442 if (OvIItr != OverloadIntrinsics.end()) { 443 const RVVOverloadIntrinsicDef &OvIntrinsicDef = OvIItr->second; 444 for (auto Index : OvIntrinsicDef.Indexes) 445 CreateRVVIntrinsicDecl(LR, II, PP, Index, 446 /*IsOverload*/ true); 447 448 // If we added overloads, need to resolve the lookup result. 449 LR.resolveKind(); 450 return true; 451 } 452 453 // Lookup the function name from the intrinsics. 454 auto Itr = Intrinsics.find(Name); 455 if (Itr != Intrinsics.end()) { 456 CreateRVVIntrinsicDecl(LR, II, PP, Itr->second, 457 /*IsOverload*/ false); 458 return true; 459 } 460 461 // It's not an RVV intrinsics. 462 return false; 463 } 464 465 namespace clang { 466 std::unique_ptr<clang::sema::RISCVIntrinsicManager> 467 CreateRISCVIntrinsicManager(Sema &S) { 468 return std::make_unique<RISCVIntrinsicManagerImpl>(S); 469 } 470 } // namespace clang 471