diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -90,3 +90,6 @@ clang_tablegen(riscv_vector_builtin_cg.inc -gen-riscv-vector-builtin-codegen SOURCE riscv_vector.td TARGET ClangRISCVVectorBuiltinCG) +clang_tablegen(riscv_vector_builtin_sema.inc -gen-riscv-vector-builtin-sema + SOURCE riscv_vector.td + TARGET ClangRISCVVectorBuiltinSema) diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -905,6 +905,9 @@ // Annotation for the attribute pragma directives - #pragma clang attribute ... PRAGMA_ANNOTATION(pragma_attribute) +// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic .. +PRAGMA_ANNOTATION(pragma_riscv) + // Annotations for module import translated from #include etc. ANNOTATION(module_include) ANNOTATION(module_begin) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -213,6 +213,7 @@ std::unique_ptr AttributePragmaHandler; std::unique_ptr MaxTokensHerePragmaHandler; std::unique_ptr MaxTokensTotalPragmaHandler; + std::unique_ptr RISCVPragmaHandler; std::unique_ptr CommentSemaHandler; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1576,6 +1576,9 @@ /// assignment. llvm::DenseMap RefsMinusAssignments; + /// Indicate RVV builtin funtions enabled or not. + bool DeclareRVVBuiltins = false; + private: Optional> CachedDarwinSDKInfo; @@ -13358,6 +13361,8 @@ llvm::StringRef StackSlotLabel, AlignPackInfo Value); +bool GetRVVBuiltinInfo(Sema &S, LookupResult &LR, IdentifierInfo *II, + Preprocessor &PP); } // end namespace clang namespace llvm { diff --git a/clang/include/clang/Support/RISCVVIntrinsicUtils.h b/clang/include/clang/Support/RISCVVIntrinsicUtils.h --- a/clang/include/clang/Support/RISCVVIntrinsicUtils.h +++ b/clang/include/clang/Support/RISCVVIntrinsicUtils.h @@ -218,6 +218,12 @@ bool isFloat(unsigned Width) const { return isFloat() && ElementBitwidth == Width; } + bool isConstant() const { return IsConstant; } + bool isPointer() const { return IsPointer; } + unsigned getElementBitwidth() const { return ElementBitwidth; } + + ScalarTypeKind getScalarType() const { return ScalarType; } + VScaleVal getScale() const { return Scale; } private: // Verify RVV vector type and set Valid. @@ -255,18 +261,6 @@ TypeProfile Proto); }; -using RISCVPredefinedMacroT = uint8_t; - -enum RISCVPredefinedMacro : RISCVPredefinedMacroT { - Basic = 0, - V = 1 << 1, - Zvfh = 1 << 2, - RV64 = 1 << 3, - VectorMaxELen64 = 1 << 4, - VectorMaxELenFp32 = 1 << 5, - VectorMaxELenFp64 = 1 << 6, -}; - enum PolicyScheme : uint8_t { SchemeNone, HasPassthruOperand, @@ -294,7 +288,6 @@ // The types we use to obtain the specific LLVM intrinsic. They are index of // InputTypes. -1 means the return type. std::vector IntrinsicTypes; - RISCVPredefinedMacroT RISCVPredefinedMacros = 0; unsigned NF = 1; public: @@ -323,9 +316,6 @@ llvm::StringRef getIRName() const { return IRName; } llvm::StringRef getManualCodegen() const { return ManualCodegen; } PolicyScheme getPolicyScheme() const { return Scheme; } - RISCVPredefinedMacroT getRISCVPredefinedMacros() const { - return RISCVPredefinedMacros; - } unsigned getNF() const { return NF; } const std::vector &getIntrinsicTypes() const { return IntrinsicTypes; @@ -336,7 +326,7 @@ static std::string getSuffixStr(BasicType Type, int Log2LMUL, - const llvm::SmallVector &TypeProfiles); + const llvm::ArrayRef &TypeProfiles); }; } // end namespace RISCV diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -356,6 +356,16 @@ Token &FirstToken) override; }; +struct PragmaRISCVHandler : public PragmaHandler { + PragmaRISCVHandler(Sema &Actions) + : PragmaHandler("riscv"), Actions(Actions) {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; + +private: + Sema &Actions; +}; + void markAsReinjectedForRelexing(llvm::MutableArrayRef Toks) { for (auto &T : Toks) T.setFlag(clang::Token::IsReinjected); @@ -495,6 +505,11 @@ MaxTokensTotalPragmaHandler = std::make_unique(); PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); + + if (getTargetInfo().getTriple().isRISCV()) { + RISCVPragmaHandler = std::make_unique(Actions); + PP.AddPragmaHandler("clang", RISCVPragmaHandler.get()); + } } void Parser::resetPragmaHandlers() { @@ -615,6 +630,11 @@ PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); MaxTokensTotalPragmaHandler.reset(); + + if (getTargetInfo().getTriple().isRISCV()) { + PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get()); + RISCVPragmaHandler.reset(); + } } /// Handle the annotation token produced for #pragma unused(...) @@ -3830,3 +3850,34 @@ PP.overrideMaxTokens(MaxTokens, Loc); } + +// Handle '#pragma clang riscv intrinsic vector'. +void PragmaRISCVHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &FirstToken) { + Token Tok; + PP.Lex(Tok); + IdentifierInfo *II = Tok.getIdentifierInfo(); + if (!II || (!II->isStr("intrinsic"))) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument) + << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'intrinsic'"; + return; + } + + PP.Lex(Tok); + II = Tok.getIdentifierInfo(); + if (!II || (!II->isStr("vector"))) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument) + << PP.getSpelling(Tok) << "riscv" << /*Expected=*/true << "'vector'"; + return; + } + + PP.Lex(Tok); + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "clang riscv intrinsic"; + return; + } + + Actions.DeclareRVVBuiltins = true; +} diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -46,6 +46,7 @@ SemaInit.cpp SemaLambda.cpp SemaLookup.cpp + SemaRVVLookup.cpp SemaModule.cpp SemaObjCProperty.cpp SemaOpenMP.cpp @@ -73,4 +74,5 @@ clangBasic clangEdit clangLex + clangSupport ) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -928,6 +928,12 @@ } } + if (DeclareRVVBuiltins) { + if (GetRVVBuiltinInfo(*this, R, II, PP)) { + return true; + } + } + // If this is a builtin on this (or all) targets, create the decl. if (unsigned BuiltinID = II->getBuiltinID()) { // In C++, C2x, and OpenCL (spec v1.2 s6.9.f), we don't have any diff --git a/clang/lib/Sema/SemaRVVLookup.cpp b/clang/lib/Sema/SemaRVVLookup.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Sema/SemaRVVLookup.cpp @@ -0,0 +1,469 @@ +//===-- SemaRVVLookup.cpp - Name Lookup for RISC-V Vector Intrinsic -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements name lookup for RISC-V vector intrinsic. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclLookups.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/TargetBuiltins.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/ModuleLoader.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" +#include "clang/Sema/Scope.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaInternal.h" +#include "clang/Sema/TemplateDeduction.h" +#include "clang/Sema/TypoCorrection.h" +#include "clang/Support/RISCVVIntrinsicUtils.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/TinyPtrVector.h" +#include "llvm/ADT/edit_distance.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace clang; +using namespace clang::RISCV; + +namespace { + +// RVVRequire should be sync with target features, but only +// required features used in riscv_vector.td. +enum class RVVRequire : uint8_t { + None = 0, + RV64 = 1 << 0, + FullMultiply = 1 << 1, + + LLVM_MARK_AS_BITMASK_ENUM(FullMultiply) +}; + +// Raw RVV intrinsic info, used to expand later. +// This struct is highly compact for minimized code size, and should sync +// with RISCVVEmitter.cpp. +struct RVVIntrinsicRecord { + // Intrinsic name, e.g. vadd_vv + const char *Name; + + // Overloaded intrinsic name, could be empty if can be computed from Name + // e.g. vadd + const char *OverloadedName; + + // Prototype for this intrinsic, index of RVVSignatureTable. + uint16_t PrototypeIndex; + + // Prototype for masked intrinsic, index of RVVSignatureTable. + uint16_t MaskedPrototypeIndex; + + // Suffix of intrinsic name, index of RVVSignatureTable. + uint16_t SuffixIndex; + + // Suffix of overloaded intrinsic name, index of RVVSignatureTable. + uint16_t OverloadedSuffixIndex; + + // Length of the prototype. + uint8_t PrototypeLength; + + // Length of prototype of masked intrinsic. + uint8_t MaskedPrototypeLength; + + // Length of intrinsic name suffix. + uint8_t SuffixLength; + + // Length of overloaded intrinsic suffix. + uint8_t OverloadedSuffixSize; + + // Required target features for this intrinsic. + RVVRequire RequiredExtension; + + // Supported type, mask of BasicType + uint8_t TypeRangeMask; + + // Supported LMUL. + uint8_t Log2LMULMask; + + // Number of field, large than 1 if it's segment load/store. + uint8_t NF; +}; + +// Function definition of a RVV intrinsic +struct RVVIntrinsicDef { + /// Full function name with suffix, e.g. vadd_vv_i32m1. + std::string Name; + + /// Overloaded function name, e.g. vadd. + std::string OverloadName; + + /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd + std::string BuiltinName; + + /// Function signature, first element is return type. + RVVTypes Signature; +}; + +struct RVVOverloadIntrinsicDef { + // Index of RVVIntrinsicManager::IntrinsicList. + SmallVector Indexs; +}; + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); +} // namespace + +static const TypeProfile RVVSignatureTable[] = { +#define DECL_SIGNATURE_TABLE +#include "clang/Basic/riscv_vector_builtin_sema.inc" +#undef DECL_SIGNATURE_TABLE +}; + +static const RVVIntrinsicRecord RVVIntrinsicRecords[] = { +#define DECL_INTRINSIC_RECORDS +#include "clang/Basic/riscv_vector_builtin_sema.inc" +#undef DECL_INTRINSIC_RECORDS +}; + +static ArrayRef ProtoSeq2ArrayRef(uint16_t Index, uint8_t Length) { + return ArrayRef(&RVVSignatureTable[Index], Length); +} + +static QualType RVVType2Qual(ASTContext &Context, const RVVType *Type) { + QualType QT; + switch (Type->getScalarType()) { + case ScalarTypeKind::Void: + QT = Context.VoidTy; + break; + case ScalarTypeKind::Size_t: + QT = Context.getSizeType(); + break; + case ScalarTypeKind::Ptrdiff_t: + QT = Context.getPointerDiffType(); + break; + case ScalarTypeKind::UnsignedLong: + QT = Context.UnsignedLongTy; + break; + case ScalarTypeKind::SignedLong: + QT = Context.LongTy; + break; + case ScalarTypeKind::Boolean: + QT = Context.BoolTy; + break; + case ScalarTypeKind::SignedInteger: + QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), true); + break; + case ScalarTypeKind::UnsignedInteger: + QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), false); + break; + case ScalarTypeKind::Float: + switch (Type->getElementBitwidth()) { + case 64: + QT = Context.DoubleTy; + break; + case 32: + QT = Context.FloatTy; + break; + case 16: + QT = Context.Float16Ty; + break; + } + break; + default: + return QT; + } + if (Type->isVector()) + QT = Context.getScalableVectorType(QT, Type->getScale().getValue()); + + if (Type->isConstant()) { + QT = Context.getConstType(QT); + } + + // Transform the type to a pointer as the last step, if necessary. + if (Type->isPointer()) { + QT = Context.getPointerType(QT); + } + + return QT; +} + +static void +CreateRVVIntrinsicDecl(Sema &S, LookupResult &LR, IdentifierInfo *II, + Preprocessor &PP, unsigned Index, + std::vector &RVVIntrinsicList, + bool IsOverload) { + ASTContext &Context = S.Context; + RVVIntrinsicDef &IDef = RVVIntrinsicList[Index]; + auto Sigs = IDef.Signature; + size_t SigLength = Sigs.size(); + auto ReturnType = Sigs[0]; + QualType RetType = RVVType2Qual(Context, ReturnType); + SmallVector ArgTypes; + QualType BuiltinFuncType; + + // Skip return type, and convert RVVType to QualType for arguments. + for (size_t i = 1; i < SigLength; ++i) { + ArgTypes.push_back(RVVType2Qual(Context, Sigs[i])); + } + + FunctionProtoType::ExtProtoInfo PI( + Context.getDefaultCallingConvention(false, false, true)); + + PI.Variadic = false; + + SourceLocation Loc = LR.getNameLoc(); + BuiltinFuncType = Context.getFunctionType(RetType, ArgTypes, PI); + DeclContext *Parent = Context.getTranslationUnitDecl(); + + FunctionDecl *RVVIntrinsicDecl = FunctionDecl::Create( + Context, Parent, Loc, Loc, II, BuiltinFuncType, /*TInfo=*/nullptr, + SC_Extern, S.getCurFPFeatures().isFPConstrained(), false, + BuiltinFuncType->isFunctionProtoType()); + + // Create Decl objects for each parameter, adding them to the + // FunctionDecl. + const auto *FP = cast(BuiltinFuncType); + SmallVector ParmList; + for (unsigned IParm = 0, e = FP->getNumParams(); IParm != e; ++IParm) { + ParmVarDecl *Parm = + ParmVarDecl::Create(Context, RVVIntrinsicDecl, Loc, Loc, nullptr, + FP->getParamType(IParm), nullptr, SC_None, nullptr); + Parm->setScopeInfo(0, IParm); + ParmList.push_back(Parm); + } + RVVIntrinsicDecl->setParams(ParmList); + + // Add function attributes. + if (IsOverload) + RVVIntrinsicDecl->addAttr(OverloadableAttr::CreateImplicit(Context)); + + // Setup alias to __builtin_rvv_* + auto &IntrinsicII = PP.getIdentifierTable().get(IDef.BuiltinName); + RVVIntrinsicDecl->addAttr( + BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII)); + + // Add to symbol table. + LR.addDecl(RVVIntrinsicDecl); +} + +namespace { +class RVVIntrinsicManager { +private: + // List of all RVV intrinsic. + std::vector IntrinsicList; + // Mapping function name to index of IntrinsicList. + StringMap Intrinsics; + // Mapping function name to RVVOverloadIntrinsicDef. + StringMap OverloadIntrinsics; + + ASTContext &Context; + + // Create IntrinsicList + void InitIntrinsicList(); + + // Create RVVIntrinsicDef. + void InitRVVIntrinsic(const RVVIntrinsicRecord &Record, StringRef SuffixStr, + StringRef OverloadedSuffixStr, bool IsMask, + RVVTypes &Types); + +public: + RVVIntrinsicManager(ASTContext &Context) : Context(Context) { + InitIntrinsicList(); + } + + bool CreateIntrinsicIfFound(Sema &S, LookupResult &LR, IdentifierInfo *II, + Preprocessor &PP); +}; +} // namespace + +void RVVIntrinsicManager::InitIntrinsicList() { + const TargetInfo &TI = Context.getTargetInfo(); + bool HasVectorFloat32 = TI.hasFeature("zve32f"); + bool HasVectorFloat64 = TI.hasFeature("zve64d"); + bool HasZvfh = TI.hasFeature("experimental-zvfh"); + bool HasRV64 = TI.hasFeature("64bit"); + bool HasFullMultiply = TI.hasFeature("v"); + + for (auto &Record : RVVIntrinsicRecords) { + // Create Intrinsics for each type and LMUL. + BasicType BaseType = BasicType::Unknown; + auto ProtoSeq = + ProtoSeq2ArrayRef(Record.PrototypeIndex, Record.PrototypeLength); + auto ProtoMaskSeq = ProtoSeq2ArrayRef(Record.MaskedPrototypeIndex, + Record.MaskedPrototypeLength); + auto SuffixProto = + ProtoSeq2ArrayRef(Record.SuffixIndex, Record.SuffixLength); + auto MangledSuffixProto = ProtoSeq2ArrayRef(Record.OverloadedSuffixIndex, + Record.OverloadedSuffixSize); + for (int TypeRangeMaskShift = 0; + TypeRangeMaskShift <= static_cast(BasicType::MaxOffset); + ++TypeRangeMaskShift) { + int BaseTypeI = 1 << TypeRangeMaskShift; + BaseType = static_cast(BaseTypeI); + + if ((BaseTypeI & Record.TypeRangeMask) != BaseTypeI) + continue; + + // Check requirement. + if (BaseType == BasicType::Float16 && !HasZvfh) + continue; + + if (BaseType == BasicType::Float32 && !HasVectorFloat32) + continue; + + if (BaseType == BasicType::Float64 && !HasVectorFloat64) + continue; + + if (((Record.RequiredExtension & RVVRequire::RV64) == RVVRequire::RV64) && + !HasRV64) + continue; + + if ((BaseType == BasicType::Int64) && + ((Record.RequiredExtension & RVVRequire::FullMultiply) == + RVVRequire::FullMultiply) && + !HasFullMultiply) + continue; + + for (int Log2LMUL = -3; Log2LMUL <= 3; Log2LMUL++) { + if (!(Record.Log2LMULMask & (1 << (Log2LMUL + 3)))) { + continue; + } + Optional Types = + RVVType::computeTypes(BaseType, Log2LMUL, Record.NF, ProtoSeq); + + // Ignored to create new intrinsic if there are any illegal types. + if (!Types.hasValue()) { + continue; + } + + auto SuffixStr = + RVVIntrinsic::getSuffixStr(BaseType, Log2LMUL, SuffixProto); + auto OverloadedSuffixStr = + RVVIntrinsic::getSuffixStr(BaseType, Log2LMUL, MangledSuffixProto); + + // Create non-masked intrinsic. + InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, false, *Types); + + bool HasMask = Record.MaskedPrototypeLength != 0; + + if (HasMask) { + // Create masked intrinsic. + Optional MaskTypes = RVVType::computeTypes( + BaseType, Log2LMUL, Record.NF, ProtoMaskSeq); + + InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, true, + *MaskTypes); + } + } + } + } +} + +// Compute name and signatures for intrinsic with practical types. +void RVVIntrinsicManager::InitRVVIntrinsic(const RVVIntrinsicRecord &Record, + StringRef SuffixStr, + StringRef OverloadedSuffixStr, + bool IsMask, RVVTypes &Signature) { + // Function name, e.g. vadd_vv_i32m1. + std::string Name = Record.Name; + if (!SuffixStr.empty()) + Name += "_" + SuffixStr.str(); + + if (IsMask) { + Name += "_m"; + } + + // Overloaded function name, e.g. vadd. + std::string OverloadedName; + if (!Record.OverloadedName) + OverloadedName = StringRef(Record.Name).split("_").first.str(); + else + OverloadedName = Record.OverloadedName; + if (!OverloadedSuffixStr.empty()) + OverloadedName += "_" + OverloadedSuffixStr.str(); + + // clang built-in function name, e.g. __builtin_rvv_vadd. + std::string BuiltinName = "__builtin_rvv_" + std::string(Record.Name); + if (IsMask) { + BuiltinName += "_m"; + } + + // Put into IntrinsicList. + size_t Index = IntrinsicList.size(); + IntrinsicList.push_back({Name, OverloadedName, BuiltinName, Signature}); + + // Creating mapping to Intrinsics. + Intrinsics.insert({Name, Index}); + + // Get the RVVOverloadIntrinsicDef. + RVVOverloadIntrinsicDef &OverloadIntrinsicDef = + OverloadIntrinsics[OverloadedName]; + + // And added the index. + OverloadIntrinsicDef.Indexs.push_back(Index); +} + +bool RVVIntrinsicManager::CreateIntrinsicIfFound(Sema &S, LookupResult &LR, + IdentifierInfo *II, + Preprocessor &PP) { + StringRef Name = II->getName(); + + // Lookup the function name from the overload intrinsics first. + auto OvIItr = OverloadIntrinsics.find(Name); + if (OvIItr != OverloadIntrinsics.end()) { + auto OvIntrinsicDef = OvIItr->second; + for (auto Index : OvIntrinsicDef.Indexs) { + CreateRVVIntrinsicDecl(S, LR, II, PP, Index, IntrinsicList, + /*IsOverload*/ true); + } + + // If we added overloads, need to resolve the lookup result. + LR.resolveKind(); + return true; + } + + // Lookup the function name from the intrinsics. + auto Itr = Intrinsics.find(Name); + if (Itr != Intrinsics.end()) { + CreateRVVIntrinsicDecl(S, LR, II, PP, Itr->second, IntrinsicList, + /*IsOverload*/ false); + return true; + } + + // It's not RVV intrinsics. + return false; +} + +namespace clang { +bool GetRVVBuiltinInfo(Sema &S, LookupResult &LR, IdentifierInfo *II, + Preprocessor &PP) { + static std::unique_ptr IntrinsicManager = + std::make_unique(S.Context); + + return IntrinsicManager->CreateIntrinsicIfFound(S, LR, II, PP); +} +} // namespace clang diff --git a/clang/lib/Support/RISCVVIntrinsicUtils.cpp b/clang/lib/Support/RISCVVIntrinsicUtils.cpp --- a/clang/lib/Support/RISCVVIntrinsicUtils.cpp +++ b/clang/lib/Support/RISCVVIntrinsicUtils.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/raw_ostream.h" #include @@ -853,27 +854,6 @@ Name += "_m"; } - // Init RISC-V extensions - for (const auto &T : OutInTypes) { - if (T->isFloatVector(16) || T->isFloat(16)) - RISCVPredefinedMacros |= RISCVPredefinedMacro::Zvfh; - if (T->isFloatVector(32)) - RISCVPredefinedMacros |= RISCVPredefinedMacro::VectorMaxELenFp32; - if (T->isFloatVector(64)) - RISCVPredefinedMacros |= RISCVPredefinedMacro::VectorMaxELenFp64; - if (T->isVector(64)) - RISCVPredefinedMacros |= RISCVPredefinedMacro::VectorMaxELen64; - } - for (auto Feature : RequiredFeatures) { - if (Feature == "RV64") - RISCVPredefinedMacros |= RISCVPredefinedMacro::RV64; - // Note: Full multiply instruction (mulh, mulhu, mulhsu, smul) for EEW=64 - // require V. - if (Feature == "FullMultiply" && - (RISCVPredefinedMacros & RISCVPredefinedMacro::VectorMaxELen64)) - RISCVPredefinedMacros |= RISCVPredefinedMacro::V; - } - // Init OutputType and InputTypes OutputType = OutInTypes[0]; InputTypes.assign(OutInTypes.begin() + 1, OutInTypes.end()); @@ -901,7 +881,7 @@ std::string RVVIntrinsic::getSuffixStr(BasicType Type, int Log2LMUL, - const llvm::SmallVector &TypeProfiles) { + const llvm::ArrayRef &TypeProfiles) { SmallVector SuffixStrs; for (auto TP : TypeProfiles) { auto T = RVVType::computeType(Type, Log2LMUL, TP); diff --git a/clang/utils/TableGen/RISCVVEmitter.cpp b/clang/utils/TableGen/RISCVVEmitter.cpp --- a/clang/utils/TableGen/RISCVVEmitter.cpp +++ b/clang/utils/TableGen/RISCVVEmitter.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" @@ -29,9 +30,48 @@ using namespace clang::RISCV; namespace { +struct SemaRecord { + // Intrinsic name, e.g. vadd_vv + std::string Name; + + // Overloaded intrinsic name, could be empty if can be computed from Name + // e.g. vadd + std::string OverloadedName; + + // Supported type, mask of BasicType + unsigned TypeRangeMask; + + // Supported LMUL. + unsigned Log2LMULMask; + + // Required target features for this intrinsic. + std::vector RequiredFeatures; + + // Prototype for this intrinsic. + SmallVector Prototype; + + // Prototype for masked intrinsic. + SmallVector MaskedPrototype; + + // Suffix of intrinsic name. + SmallVector Suffix; + + // Suffix of overloaded intrinsic name. + SmallVector OverloadedSuffix; + + // Number of field, large than 1 if it's segment load/store. + unsigned NF; +}; + class RVVEmitter { private: RecordKeeper &Records; + // Concat BasicType, LMUL and Proto as key + StringMap LegalTypes; + StringSet<> IllegalTypes; + + std::vector SemaRecords; + std::vector SemaSignatureTable; public: RVVEmitter(RecordKeeper &R) : Records(R) {} @@ -45,22 +85,27 @@ /// Emit all the information needed to map builtin -> LLVM IR intrinsic. void createCodeGen(raw_ostream &o); + /// Emit all the information needed by SemaRVVLookup.cpp. + /// We've large number of intrinsic function for RVV, creating a customized + /// could speed up the compilation time. + void createSema(raw_ostream &o); + private: /// Create all intrinsics and add them to \p Out void createRVVIntrinsics(std::vector> &Out); /// Print HeaderCode in RVVHeader Record to \p Out void printHeaderCode(raw_ostream &OS); - /// Emit Acrh predecessor definitions and body, assume the element of Defs are - /// sorted by extension. - void emitArchMacroAndBody( - std::vector> &Defs, raw_ostream &o, - std::function); + /// Construct a compressed signature table used for createSema. + void ConstructSemaSignatureTable(); + /// Get index from the signature table. + unsigned GetSemaSignatureIndex(const SmallVector &Signature); + + /// Emit all SemaRecord for SemaRVVLookup.cpp. + void EmitSemaRecords(raw_ostream &OS); - // Emit the architecture preprocessor definitions. Return true when emits - // non-empty string. - bool emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, - raw_ostream &o); + /// Emit signature table for SemaRVVLookup.cpp. + void EmitSemaSignatureTable(raw_ostream &OS); }; } // namespace @@ -141,35 +186,6 @@ OS << " break;\n"; } -void emitIntrinsicFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) { - OS << "__attribute__((__clang_builtin_alias__("; - OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n"; - OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getName() << "("; - // Emit function arguments - const RVVTypes &InputTypes = RVVI.getInputTypes(); - if (!InputTypes.empty()) { - ListSeparator LS; - for (unsigned i = 0; i < InputTypes.size(); ++i) - OS << LS << InputTypes[i]->getTypeStr(); - } - OS << ");\n"; -} - -void emitMangledFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) { - OS << "__attribute__((__clang_builtin_alias__("; - OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n"; - OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getMangledName() - << "("; - // Emit function arguments - const RVVTypes &InputTypes = RVVI.getInputTypes(); - if (!InputTypes.empty()) { - ListSeparator LS; - for (unsigned i = 0; i < InputTypes.size(); ++i) - OS << LS << InputTypes[i]->getTypeStr(); - } - OS << ");\n"; -} - //===----------------------------------------------------------------------===// // RVVEmitter implementation //===----------------------------------------------------------------------===// @@ -202,10 +218,9 @@ OS << "extern \"C\" {\n"; OS << "#endif\n\n"; - printHeaderCode(OS); + OS << "#pragma clang riscv intrinsic vector\n\n"; - std::vector> Defs; - createRVVIntrinsics(Defs); + printHeaderCode(OS); auto printType = [&](auto T) { OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr() @@ -260,37 +275,8 @@ } OS << "#endif\n\n"; - // The same extension include in the same arch guard marco. - llvm::stable_sort(Defs, [](const std::unique_ptr &A, - const std::unique_ptr &B) { - return A->getRISCVPredefinedMacros() < B->getRISCVPredefinedMacros(); - }); - - OS << "#define __rvv_ai static __inline__\n"; - - // Print intrinsic functions with macro - emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { - OS << "__rvv_ai "; - emitIntrinsicFuncDef(Inst, OS); - }); - - OS << "#undef __rvv_ai\n\n"; - OS << "#define __riscv_v_intrinsic_overloading 1\n"; - // Print Overloaded APIs - OS << "#define __rvv_aio static __inline__ " - "__attribute__((__overloadable__))\n"; - - emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { - if (!Inst.isMasked() && !Inst.hasUnMaskedOverloaded()) - return; - OS << "__rvv_aio "; - emitMangledFuncDef(Inst, OS); - }); - - OS << "#undef __rvv_aio\n"; - OS << "\n#ifdef __cplusplus\n"; OS << "}\n"; OS << "#endif // __cplusplus\n"; @@ -383,9 +369,9 @@ std::vector RV = Records.getAllDerivedDefinitions("RVVBuiltin"); for (auto *R : RV) { StringRef Name = R->getValueAsString("Name"); - StringRef SuffixProto = R->getValueAsString("Suffix"); + StringRef Suffix = R->getValueAsString("Suffix"); StringRef MangledName = R->getValueAsString("MangledName"); - StringRef MangledSuffixProto = R->getValueAsString("MangledSuffix"); + StringRef OverloadedSuffix = R->getValueAsString("MangledSuffix"); StringRef Prototypes = R->getValueAsString("Prototype"); StringRef TypeRange = R->getValueAsString("TypeRange"); bool HasMasked = R->getValueAsBit("HasMasked"); @@ -411,29 +397,30 @@ unsigned NF = R->getValueAsInt("NF"); // Parse prototype and create a list of primitive type with transformers - // (operand) in ProtoSeq. ProtoSeq[0] is output operand. - SmallVector ProtoSeq = parsePrototypes(Prototypes); + // (operand) in Prototype. Prototype[0] is output operand. + SmallVector Prototype = parsePrototypes(Prototypes); - SmallVector SuffixProtoSeq = parsePrototypes(SuffixProto); - SmallVector MangledSuffixProtoSeq = - parsePrototypes(MangledSuffixProto); + SmallVector Suffixtype = parsePrototypes(Suffix); + SmallVector OverloadedSuffixtype = + parsePrototypes(OverloadedSuffix); // Compute Builtin types - SmallVector ProtoMaskSeq = ProtoSeq; + SmallVector MaskedPrototype = Prototype; if (HasMasked) { // If HasMaskedOffOperand, insert result type as first input operand. if (HasMaskedOffOperand) { if (NF == 1) { - ProtoMaskSeq.insert(ProtoMaskSeq.begin() + 1, ProtoSeq[0]); + MaskedPrototype.insert(MaskedPrototype.begin() + 1, Prototype[0]); } else { // Convert // (void, op0 address, op1 address, ...) // to // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...) - TypeProfile MaskoffType = ProtoSeq[1]; + TypeProfile MaskoffType = Prototype[1]; MaskoffType.TM &= ~static_cast(TypeModifier::Pointer); for (unsigned I = 0; I < NF; ++I) - ProtoMaskSeq.insert(ProtoMaskSeq.begin() + NF + 1, MaskoffType); + MaskedPrototype.insert(MaskedPrototype.begin() + NF + 1, + MaskoffType); } } if (HasMaskedOffOperand && NF > 1) { @@ -442,16 +429,17 @@ // to // (void, op0 address, op1 address, ..., mask, maskedoff0, maskedoff1, // ...) - ProtoMaskSeq.insert(ProtoMaskSeq.begin() + NF + 1, TypeProfile::Mask); + MaskedPrototype.insert(MaskedPrototype.begin() + NF + 1, + TypeProfile::Mask); } else { // If HasMasked, insert TypeProfile:Mask as first input operand. - ProtoMaskSeq.insert(ProtoMaskSeq.begin() + 1, TypeProfile::Mask); + MaskedPrototype.insert(MaskedPrototype.begin() + 1, TypeProfile::Mask); } } // If HasVL, append TypeProfile:VL to last operand if (HasVL) { - ProtoSeq.push_back(TypeProfile::VL); - ProtoMaskSeq.push_back(TypeProfile::VL); + Prototype.push_back(TypeProfile::VL); + MaskedPrototype.push_back(TypeProfile::VL); } // Create Intrinsics for each type and LMUL. @@ -459,15 +447,14 @@ for (int Log2LMUL : Log2LMULList) { BasicType BT = ParseBasicType(I); Optional Types = - RVVType::computeTypes(BT, Log2LMUL, NF, ProtoSeq); + RVVType::computeTypes(BT, Log2LMUL, NF, Prototype); // Ignored to create new intrinsic if there are any illegal types. if (!Types.hasValue()) continue; - auto SuffixStr = - RVVIntrinsic::getSuffixStr(BT, Log2LMUL, SuffixProtoSeq); + auto SuffixStr = RVVIntrinsic::getSuffixStr(BT, Log2LMUL, Suffixtype); auto MangledSuffixStr = - RVVIntrinsic::getSuffixStr(BT, Log2LMUL, MangledSuffixProtoSeq); + RVVIntrinsic::getSuffixStr(BT, Log2LMUL, OverloadedSuffixtype); // Create a unmasked intrinsic Out.push_back(std::make_unique( Name, SuffixStr, MangledName, MangledSuffixStr, IRName, @@ -478,7 +465,7 @@ if (HasMasked) { // Create a masked intrinsic Optional MaskTypes = - RVVType::computeTypes(BT, Log2LMUL, NF, ProtoMaskSeq); + RVVType::computeTypes(BT, Log2LMUL, NF, MaskedPrototype); Out.push_back(std::make_unique( Name, SuffixStr, MangledName, MangledSuffixStr, MaskedIRName, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicy, @@ -487,6 +474,46 @@ } } // end for Log2LMULList } // end for TypeRange + + // We don't emit vsetvli and vsetvlimax for SemaRecord. + // They are handled by riscv_vector.h + if (Name == "vsetvli" || Name == "vsetvlimax") + continue; + // Create SemaRecord + SemaRecord SR; + SR.Name = Name.str(); + SR.OverloadedName = MangledName.str(); + BasicType TypeRangeMask = BasicType::Unknown; + for (char I : TypeRange) { + TypeRangeMask |= ParseBasicType(I); + } + SR.TypeRangeMask = static_cast(TypeRangeMask); + + unsigned Log2LMULMask = 0; + for (int Log2LMUL : Log2LMULList) { + Log2LMULMask |= 1 << (Log2LMUL + 3); + } + + SR.Log2LMULMask = Log2LMULMask; + for (auto RequiredFeature : RequiredFeatures) + SR.RequiredFeatures.push_back(RequiredFeature.str()); + + SR.NF = NF; + + SR.Prototype = std::move(Prototype); + + if (HasMasked) + SR.MaskedPrototype = std::move(MaskedPrototype); + + auto InitSuffixtype = [&](SmallVectorImpl &PS, + StringRef Prototypes) { + PS = parsePrototypes(Prototypes); + }; + + InitSuffixtype(SR.Suffix, Suffix); + InitSuffixtype(SR.OverloadedSuffix, OverloadedSuffix); + + SemaRecords.push_back(SR); } } @@ -499,47 +526,131 @@ } } -void RVVEmitter::emitArchMacroAndBody( - std::vector> &Defs, raw_ostream &OS, - std::function PrintBody) { - RISCVPredefinedMacroT PrevMacros = - (*Defs.begin())->getRISCVPredefinedMacros(); - bool NeedEndif = emitMacroRestrictionStr(PrevMacros, OS); - for (auto &Def : Defs) { - RISCVPredefinedMacroT CurMacros = Def->getRISCVPredefinedMacros(); - if (CurMacros != PrevMacros) { - if (NeedEndif) - OS << "#endif\n\n"; - NeedEndif = emitMacroRestrictionStr(CurMacros, OS); - PrevMacros = CurMacros; +unsigned +RVVEmitter::GetSemaSignatureIndex(const SmallVector &Signature) { + if (Signature.size() == 0) + return 0; + + // Checking Signature already in table or not. + if (Signature.size() < SemaSignatureTable.size()) { + size_t Bound = SemaSignatureTable.size() - Signature.size() + 1; + for (size_t Index = 0; Index < Bound; ++Index) { + bool Match = true; + for (size_t i = 0; i < Signature.size(); ++i) { + if (Signature[i] != SemaSignatureTable[Index + i]) { + Match = false; + break; + } + } + // Reuse if found in table. + if (Match) + return Index; } - if (Def->hasBuiltinAlias()) - PrintBody(OS, *Def); } - if (NeedEndif) - OS << "#endif\n\n"; + + // Insert Signature into SemaSignatureTable if not found in the table. + size_t Index = SemaSignatureTable.size(); + for (auto Type : Signature) { + SemaSignatureTable.push_back(Type); + } + return Index; } -bool RVVEmitter::emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, - raw_ostream &OS) { - if (PredefinedMacros == RISCVPredefinedMacro::Basic) - return false; - OS << "#if "; - ListSeparator LS(" && "); - if (PredefinedMacros & RISCVPredefinedMacro::V) - OS << LS << "defined(__riscv_v)"; - if (PredefinedMacros & RISCVPredefinedMacro::Zvfh) - OS << LS << "defined(__riscv_zvfh)"; - if (PredefinedMacros & RISCVPredefinedMacro::RV64) - OS << LS << "(__riscv_xlen == 64)"; - if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELen64) - OS << LS << "(__riscv_v_elen >= 64)"; - if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp32) - OS << LS << "(__riscv_v_elen_fp >= 32)"; - if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp64) - OS << LS << "(__riscv_v_elen_fp >= 64)"; - OS << "\n"; - return true; +void RVVEmitter::ConstructSemaSignatureTable() { + // Sort signature entries by length, let longer signature insert first, to + // make it more possible to reuse table entries, that can reduce ~10% table + // size. + struct Compare { + bool operator()(const SmallVector &A, + const SmallVector &B) { + if (A.size() != B.size()) + return A.size() > B.size(); + + size_t Len = A.size(); + for (size_t i = 0; i < Len; ++i) { + if (A[i] != B[i]) + return A[i] > B[i]; + } + + return false; + } + }; + + std::set, Compare> Signatures; + auto InsertToSignatureSet = [&](const SmallVector &Signature) { + if (Signature.empty()) + return; + + Signatures.insert(Signature); + }; + + for (auto SemaRecord : SemaRecords) { + InsertToSignatureSet(SemaRecord.Prototype); + InsertToSignatureSet(SemaRecord.MaskedPrototype); + InsertToSignatureSet(SemaRecord.Suffix); + InsertToSignatureSet(SemaRecord.OverloadedSuffix); + } + + for (auto Sig : Signatures) { + GetSemaSignatureIndex(Sig); + } +} + +void RVVEmitter::EmitSemaSignatureTable(raw_ostream &OS) { + OS << "#ifdef DECL_SIGNATURE_TABLE\n"; + for (auto Sig : SemaSignatureTable) + OS << "TypeProfile(" << static_cast(Sig.PT) << ", " + << static_cast(Sig.VTM) << ", " << static_cast(Sig.TM) + << "),\n"; + OS << "#endif\n"; +} + +void RVVEmitter::EmitSemaRecords(raw_ostream &OS) { + OS << "#ifdef DECL_INTRINSIC_RECORDS\n"; + for (auto SR : SemaRecords) { + // Output *MUST* sync with RVVIntrinsicRecord in SemaRVVLookup.cpp. + OS << "{" + << "\"" << SR.Name << "\", "; + + if (SR.OverloadedName.empty()) + OS << "nullptr, "; + else + OS << "\"" << SR.OverloadedName << "\", "; + + OS << GetSemaSignatureIndex(SR.Prototype) << ", "; + OS << GetSemaSignatureIndex(SR.MaskedPrototype) << ", "; + OS << GetSemaSignatureIndex(SR.Suffix) << ", "; + OS << GetSemaSignatureIndex(SR.OverloadedSuffix) << ", "; + + OS << SR.Prototype.size() << ", "; + OS << SR.MaskedPrototype.size() << ", "; + OS << SR.Suffix.size() << ", "; + OS << SR.OverloadedSuffix.size() << ", "; + + if (SR.RequiredFeatures.empty()) + OS << "RVVRequire::None"; + else { + ListSeparator LS(" | "); + for (auto RequiredFeature : SR.RequiredFeatures) + OS << LS << "RVVRequire::" << RequiredFeature; + } + OS << ","; + + OS << " /* Type Range Mask*/" << SR.TypeRangeMask << ", "; + OS << " /* LMUL Mask = */ " << SR.Log2LMULMask << ", "; + + OS << SR.NF << "},\n"; + } + OS << "#endif\n"; +} + +void RVVEmitter::createSema(raw_ostream &OS) { + std::vector> Defs; + createRVVIntrinsics(Defs); + + ConstructSemaSignatureTable(); + EmitSemaSignatureTable(OS); + EmitSemaRecords(OS); } namespace clang { @@ -555,4 +666,8 @@ RVVEmitter(Records).createCodeGen(OS); } +void EmitRVVBuiltinSema(RecordKeeper &Records, raw_ostream &OS) { + RVVEmitter(Records).createSema(OS); +} + } // End namespace clang diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -88,6 +88,7 @@ GenRISCVVectorHeader, GenRISCVVectorBuiltins, GenRISCVVectorBuiltinCG, + GenRISCVVectorBuiltinSema, GenAttrDocs, GenDiagDocs, GenOptDocs, @@ -243,6 +244,8 @@ "Generate riscv_vector_builtins.inc for clang"), clEnumValN(GenRISCVVectorBuiltinCG, "gen-riscv-vector-builtin-codegen", "Generate riscv_vector_builtin_cg.inc for clang"), + clEnumValN(GenRISCVVectorBuiltinSema, "gen-riscv-vector-builtin-sema", + "Generate riscv_vector_builtin_sema.inc for clang"), clEnumValN(GenAttrDocs, "gen-attr-docs", "Generate attribute documentation"), clEnumValN(GenDiagDocs, "gen-diag-docs", @@ -458,6 +461,9 @@ case GenRISCVVectorBuiltinCG: EmitRVVBuiltinCG(Records, OS); break; + case GenRISCVVectorBuiltinSema: + EmitRVVBuiltinSema(Records, OS); + break; case GenAttrDocs: EmitClangAttrDocs(Records, OS); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -110,6 +110,7 @@ void EmitRVVHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitRVVBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitCdeHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitCdeBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);