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,430 @@ +//===-- 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 { + const char *Name; + const char *MangledName; + uint16_t ProtoSeqIndex; + uint16_t ProtoMaskSeqIndex; + uint16_t SuffixProtoIndex; + uint16_t MangledSuffixProtoIndex; + uint8_t ProtoSeqSize; + uint8_t ProtoMaskSeqSize; + uint8_t SuffixProtoSize; + uint8_t MangledSuffixProtoSize; + RVVRequire RequiredExtension; + BasicType TypeRangeMask; + uint8_t Log2LMULMask; + uint8_t NF; +}; + +// Function definition of a RVV intrinsic +struct RVVIntrinsicDef { + /// Full function name with suffix. + std::string Name; + /// Overloaded function name. + std::string OverloadName; + /// Mapping to which clang built-in function. + std::string BuiltinName; + /// Function signature, first element is return type. + RVVTypes Signature; +}; + +struct RVVOverloadIntrinsicDef { + 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 Create(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 MangledSuffixStr, 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.ProtoSeqIndex, Record.ProtoSeqSize); + auto ProtoMaskSeq = + ProtoSeq2ArrayRef(Record.ProtoMaskSeqIndex, Record.ProtoMaskSeqSize); + auto SuffixProto = + ProtoSeq2ArrayRef(Record.SuffixProtoIndex, Record.SuffixProtoSize); + auto MangledSuffixProto = ProtoSeq2ArrayRef(Record.MangledSuffixProtoIndex, + Record.MangledSuffixProtoSize); + for (int TypeRangeMaskShift = 0; + TypeRangeMaskShift <= static_cast(BasicType::MaxOffset); + ++TypeRangeMaskShift) { + BaseType = static_cast(1 << TypeRangeMaskShift); + + if ((BaseType & Record.TypeRangeMask) != BaseType) + 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 MangledSuffixStr = + RVVIntrinsic::getSuffixStr(BaseType, Log2LMUL, MangledSuffixProto); + InitRVVIntrinsic(Record, SuffixStr, MangledSuffixStr, false, *Types); + bool HasMask = Record.ProtoMaskSeqSize != 0; + + if (HasMask) { + // Create masked intrinsic + Optional MaskTypes = RVVType::computeTypes( + BaseType, Log2LMUL, Record.NF, ProtoMaskSeq); + + InitRVVIntrinsic(Record, SuffixStr, MangledSuffixStr, true, + *MaskTypes); + } + } + } + } +} + +// Compute name and signatures for intrinsic with practical types. +void RVVIntrinsicManager::InitRVVIntrinsic(const RVVIntrinsicRecord &Record, + StringRef SuffixStr, + StringRef MangledSuffixStr, + 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 MangledName; + if (!Record.MangledName) + MangledName = StringRef(Record.Name).split("_").first.str(); + else + MangledName = Record.MangledName; + if (!MangledSuffixStr.empty()) + MangledName += "_" + MangledSuffixStr.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, MangledName, BuiltinName, Signature}); + + // Creating mapping to Intrinsics. + Intrinsics.insert({Name, Index}); + + // Get the RVVOverloadIntrinsicDef. + RVVOverloadIntrinsicDef &OverloadIntrinsicDef = + OverloadIntrinsics[MangledName]; + + // 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) { + Create(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()) { + Create(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,30 @@ using namespace clang::RISCV; namespace { +struct SemaRecord { + std::string Name; + std::string MangledName; + std::string TypeRange; + std::vector Log2LMULList; + std::vector RequiredFeatures; + + SmallVector ProtoSeq; + SmallVector ProtoMaskSeq; + SmallVector SuffixProto; + SmallVector MangledSuffixProto; + + 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 +67,20 @@ /// Emit all the information needed to map builtin -> LLVM IR intrinsic. void createCodeGen(raw_ostream &o); + /// Emit all the information needed by SemaRVVLookup.cpp. + void createSema(raw_ostream &o); + private: /// Create all intrinsics and add them to \p Out void createRVVIntrinsics(std::vector> &Out); + unsigned GetSemaSignatureIndex(const SmallVector &Signature); /// 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); + void ConstructSemaSignatureTable(); - // Emit the architecture preprocessor definitions. Return true when emits - // non-empty string. - bool emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, - raw_ostream &o); + void EmitSemaRecords(raw_ostream &OS); + void EmitSemaSignatureTable(raw_ostream &OS); }; } // namespace @@ -174,7 +194,6 @@ // RVVEmitter implementation //===----------------------------------------------------------------------===// void RVVEmitter::createHeader(raw_ostream &OS) { - OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics " "-------------------===\n" " *\n" @@ -202,10 +221,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 +278,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"; @@ -487,6 +476,36 @@ } } // 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.MangledName = MangledName.str(); + SR.TypeRange = TypeRange.str(); + SR.Log2LMULList = Log2LMULList; + for (auto RequiredFeature : RequiredFeatures) + SR.RequiredFeatures.push_back(RequiredFeature.str()); + + SR.NF = NF; + + SR.ProtoSeq = std::move(ProtoSeq); + + if (HasMasked) + SR.ProtoMaskSeq = std::move(ProtoMaskSeq); + + auto InitSuffixProtoSeq = [&](SmallVectorImpl &PS, + StringRef Prototypes) { + PS = parsePrototypes(Prototypes); + }; + + InitSuffixProtoSeq(SR.SuffixProto, SuffixProto); + InitSuffixProtoSeq(SR.MangledSuffixProto, MangledSuffixProto); + + SemaRecords.push_back(SR); } } @@ -499,47 +518,174 @@ } } -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; +void emitSemaPrototypeType(const TypeProfile &TP, raw_ostream &OS) { + OS << "TypeProfile(" << static_cast(TP.PT) << ", " + << static_cast(TP.VTM) << ", " << static_cast(TP.TM) << ")"; +} + +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.ProtoSeq); + InsertToSignatureSet(SemaRecord.ProtoMaskSeq); + InsertToSignatureSet(SemaRecord.SuffixProto); + InsertToSignatureSet(SemaRecord.MangledSuffixProto); + } + + for (auto Sig : Signatures) { + GetSemaSignatureIndex(Sig); + } +} + +void RVVEmitter::EmitSemaSignatureTable(raw_ostream &OS) { + OS << "#ifdef DECL_SIGNATURE_TABLE\n"; + for (auto Sig : SemaSignatureTable) { + emitSemaPrototypeType(Sig, OS); + OS << ",\n"; + } + OS << "#endif\n"; +} + +void RVVEmitter::EmitSemaRecords(raw_ostream &OS) { + OS << "#ifdef DECL_INTRINSIC_RECORDS\n"; + for (auto SR : SemaRecords) { + OS << "{" + << "\"" << SR.Name << "\", "; + + if (SR.MangledName.empty()) + OS << "nullptr, "; + else + OS << "\"" << SR.MangledName << "\", "; + + OS << GetSemaSignatureIndex(SR.ProtoSeq) << ", "; + OS << GetSemaSignatureIndex(SR.ProtoMaskSeq) << ", "; + OS << GetSemaSignatureIndex(SR.SuffixProto) << ", "; + OS << GetSemaSignatureIndex(SR.MangledSuffixProto) << ", "; + + OS << SR.ProtoSeq.size() << ", "; + OS << SR.ProtoMaskSeq.size() << ", "; + OS << SR.SuffixProto.size() << ", "; + OS << SR.MangledSuffixProto.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*/"; + ListSeparator TRLS(" | "); + for (auto T : SR.TypeRange) { + StringRef TypeMask; + switch (T) { + case 'c': + TypeMask = "Int8"; + break; + case 's': + TypeMask = "Int16"; + break; + case 'i': + TypeMask = "Int32"; + break; + case 'l': + TypeMask = "Int64"; + break; + case 'x': + TypeMask = "Float16"; + break; + case 'f': + TypeMask = "Float32"; + break; + case 'd': + TypeMask = "Float64"; + break; + default: + TypeMask = ""; + llvm_unreachable("Unknown TypeRang letter."); + } + OS << TRLS << "clang::RISCV::BasicType::" << TypeMask; + } + OS << ","; + OS << " /* LMUL Mask = */ "; + unsigned Log2LMULMask = 0; + for (int Log2LMUL : SR.Log2LMULList) { + Log2LMULMask |= 1 << (Log2LMUL + 3); + } + OS << 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 +701,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);