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