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 @@ -908,6 +908,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 @@ -215,6 +215,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 @@ -1582,6 +1582,9 @@ /// assignment. llvm::DenseMap RefsMinusAssignments; + /// Indicate RVV builtin funtions enabled or not. + bool DeclareRVVBuiltins = false; + private: Optional> CachedDarwinSDKInfo; @@ -13400,6 +13403,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 @@ -14,6 +14,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -224,6 +225,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. @@ -261,18 +268,6 @@ PrototypeDescriptor 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, @@ -300,7 +295,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: @@ -329,9 +323,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; @@ -345,6 +336,67 @@ llvm::ArrayRef PrototypeDescriptors); }; +// RVVRequire should be sync with target features, but only +// required features used in riscv_vector.td. +enum RVVRequire : uint8_t { + RVV_REQ_None = 0, + RVV_REQ_RV64 = 1 << 0, + RVV_REQ_FullMultiply = 1 << 1, + + LLVM_MARK_AS_BITMASK_ENUM(RVV_REQ_FullMultiply) +}; + +// Raw RVV intrinsic info, used to expand later. +// This struct is highly compact for minimized code size. +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. + uint8_t 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; +}; + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const RVVIntrinsicRecord &RVVInstrRecord); + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); } // end namespace RISCV } // end namespace clang 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); @@ -499,6 +509,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() { @@ -623,6 +638,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(...) @@ -3935,3 +3955,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,9 @@ } } + if (DeclareRVVBuiltins && 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,392 @@ +//===-- 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/Decl.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" +#include "clang/Support/RISCVVIntrinsicUtils.h" +#include "llvm/ADT/SmallVector.h" +#include +#include + +using namespace llvm; +using namespace clang; +using namespace clang::RISCV; + +namespace { + +// 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; +}; + +} // namespace + +static const PrototypeDescriptor 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 +}; + +// Get subsequence of signature table. +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; +} + +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); + + // Create FunctionDecl for a vector intrinsic. + void CreateRVVIntrinsicDecl(Sema &S, LookupResult &LR, IdentifierInfo *II, + Preprocessor &PP, unsigned Index, + bool IsOverload); + +public: + RVVIntrinsicManager(ASTContext &Context) : Context(Context) { + InitIntrinsicList(); + } + + // Create RISC-V vector intrinsic and insert into symbol table if found, and + // return true, otherwise return false. + 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 & RVV_REQ_RV64) == RVV_REQ_RV64) && + !HasRV64) + continue; + + if ((BaseType == BasicType::Int64) && + ((Record.RequiredExtension & RVV_REQ_FullMultiply) == + RVV_REQ_FullMultiply) && + !HasFullMultiply) + continue; + + // Expanded with different LMUL. + 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); +} + +void RVVIntrinsicManager::CreateRVVIntrinsicDecl(Sema &S, LookupResult &LR, + IdentifierInfo *II, + Preprocessor &PP, + unsigned Index, + bool IsOverload) { + ASTContext &Context = S.Context; + RVVIntrinsicDef &IDef = IntrinsicList[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); +} + +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, + /*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, + /*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 @@ -874,27 +875,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()); @@ -952,5 +932,29 @@ return PrototypeDescriptors; } +raw_ostream &operator<<(raw_ostream &OS, const RVVIntrinsicRecord &Record) { + OS << "{"; + OS << "\"" << Record.Name << "\","; + if (Record.OverloadedName == nullptr || + StringRef(Record.OverloadedName).empty()) + OS << "nullptr,"; + else + OS << "\"" << Record.OverloadedName << "\","; + OS << Record.PrototypeIndex << ","; + OS << Record.MaskedPrototypeIndex << ","; + OS << Record.SuffixIndex << ","; + OS << Record.OverloadedSuffixIndex << ","; + OS << (int)Record.PrototypeLength << ","; + OS << (int)Record.MaskedPrototypeLength << ","; + OS << (int)Record.SuffixLength << ","; + OS << (int)Record.OverloadedSuffixSize << ","; + OS << (int)Record.RequiredExtension << ","; + OS << (int)Record.TypeRangeMask << ","; + OS << (int)Record.Log2LMULMask << ","; + OS << (int)Record.NF << ","; + OS << "},\n"; + return OS; +} + } // end namespace RISCV } // end namespace clang 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,10 +30,46 @@ 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 extensions for this intrinsic. + unsigned RequiredExtension; + + // 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; + std::vector SemaRecords; + std::vector SemaSignatureTable; + public: RVVEmitter(RecordKeeper &R) : Records(R) {} @@ -45,22 +82,25 @@ /// 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); + /// Create all intrinsics record from RVVIntrinsics. + void createRVVIntrinsicRecord(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); - - // Emit the architecture preprocessor definitions. Return true when emits - // non-empty string. - bool emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, - raw_ostream &o); + /// Construct a compressed signature table used for createSema. + void ConstructSemaSignatureTable(); + /// Get index from the signature table. + unsigned + GetSemaSignatureIndex(const SmallVector &Signature); }; } // namespace @@ -141,35 +181,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 +213,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() @@ -263,37 +273,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"; @@ -493,6 +474,56 @@ } } // end for Log2LMULList } // end for TypeRange + + // We don't emit vsetvli and vsetvlimax for SemaRecord. + // They are written in riscv_vector.td and will emit those marco define in + // riscv_vector.h + if (Name == "vsetvli" || Name == "vsetvlimax") + continue; + + // Create SemaRecord + SemaRecord SR; + SR.Name = Name.str(); + SR.OverloadedName = OverloadedName.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; + + SR.RequiredExtension = 0; + for (auto RequiredFeature : RequiredFeatures) { + RVVRequire RequireExt = StringSwitch(RequiredFeature) + .Case("RV64", RVV_REQ_RV64) + .Case("FullMultiply", RVV_REQ_FullMultiply) + .Default(RVV_REQ_None); + assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?"); + SR.RequiredExtension |= RequireExt; + } + + 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, SuffixProto); + InitSuffixtype(SR.OverloadedSuffix, OverloadedSuffixProto); + + SemaRecords.push_back(SR); } } @@ -505,47 +536,121 @@ } } -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 (const 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 (const auto &SemaRecord : SemaRecords) { + InsertToSignatureSet(SemaRecord.Prototype); + InsertToSignatureSet(SemaRecord.MaskedPrototype); + InsertToSignatureSet(SemaRecord.Suffix); + InsertToSignatureSet(SemaRecord.OverloadedSuffix); + } + + for (const auto &Sig : Signatures) { + GetSemaSignatureIndex(Sig); + } +} + +void RVVEmitter::createRVVIntrinsicRecord( + std::vector &Out) { + ConstructSemaSignatureTable(); + + for (const auto &SR : SemaRecords) { + RVVIntrinsicRecord Record; + Record.Name = SR.Name.c_str(); + Record.OverloadedName = SR.OverloadedName.c_str(); + Record.PrototypeIndex = GetSemaSignatureIndex(SR.Prototype); + Record.MaskedPrototypeIndex = GetSemaSignatureIndex(SR.MaskedPrototype); + Record.SuffixIndex = GetSemaSignatureIndex(SR.Suffix); + Record.OverloadedSuffixIndex = GetSemaSignatureIndex(SR.OverloadedSuffix); + Record.PrototypeLength = SR.Prototype.size(); + Record.MaskedPrototypeLength = SR.MaskedPrototype.size(); + Record.SuffixLength = SR.Suffix.size(); + Record.OverloadedSuffixSize = SR.OverloadedSuffix.size(); + Record.RequiredExtension = SR.RequiredExtension; + Record.TypeRangeMask = SR.TypeRangeMask; + Record.Log2LMULMask = SR.Log2LMULMask; + Record.NF = SR.NF; + Out.push_back(Record); + } +} + +void RVVEmitter::createSema(raw_ostream &OS) { + std::vector> Defs; + std::vector RVVIntrinsicRecords; + createRVVIntrinsics(Defs); + + createRVVIntrinsicRecord(RVVIntrinsicRecords); + + // Emit signature table for SemaRVVLookup.cpp. + OS << "#ifdef DECL_SIGNATURE_TABLE\n"; + for (const auto &Sig : SemaSignatureTable) + OS << "PrototypeDescriptor(" << static_cast(Sig.PT) << ", " + << static_cast(Sig.VTM) << ", " << static_cast(Sig.TM) + << "),\n"; + OS << "#endif\n"; + + // Emit RVVIntrinsicRecords for SemaRVVLookup.cpp. + OS << "#ifdef DECL_INTRINSIC_RECORDS\n"; + for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords) + OS << Record; + OS << "#endif\n"; } namespace clang { @@ -561,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);