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 @@ -887,6 +887,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 @@ -212,6 +212,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/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(...) @@ -3798,3 +3818,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; + } + + PP.setPredefines("#define __riscv_pragma_vector_intrinsics"); +} 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 @@ -23,6 +23,8 @@ #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" @@ -48,6 +50,7 @@ #include #include "OpenCLBuiltins.inc" +#include "clang/Basic/riscv_vector_builtin_sema.inc" using namespace clang; using namespace sema; @@ -896,6 +899,126 @@ LR.resolveKind(); } +static void GetQualTypesForRVVBuiltin(Sema &S, const RVVBuiltinStruct &Builtin, + QualType &RetType, + SmallVectorImpl &ArgTypes) { + // Get the QualType instance of the return type. + unsigned Sig = RVVSignatureTable[Builtin.SigTableIndex]; + RVV2Qual(S, RVVTypeTable[Sig], RetType); + + // Get the QualType instances of the arguments. + // First type is the return type, skip it. + for (unsigned Index = 1; Index < Builtin.NumTypes; Index++) { + QualType Ty; + RVV2Qual(S, RVVTypeTable[RVVSignatureTable[Builtin.SigTableIndex + Index]], + Ty); + ArgTypes.push_back(std::move(Ty)); + } +} + +static void GetRVVBuiltinFuncType(ASTContext &Context, + QualType &BuiltinFuncType, QualType &RetType, + SmallVectorImpl &ArgTypes) { + FunctionProtoType::ExtProtoInfo PI( + Context.getDefaultCallingConvention(false, false, true)); + PI.Variadic = false; + + BuiltinFuncType = Context.getFunctionType(RetType, ArgTypes, PI); +} + +static uint32_t GetTargetFeatures(const TargetInfo &TI) { + bool HasF = TI.hasFeature("f"); + bool HasD = TI.hasFeature("d"); + bool HasZfh = TI.hasFeature("experimental-zfh"); + bool HasZvlsseg = TI.hasFeature("experimental-zvlsseg"); + uint32_t Features = 0; + if (HasF) + Features |= RISCVFeature_F; + if (HasD) + Features |= RISCVFeature_D; + if (HasZfh) + Features |= RISCVFeature_ZFH; + if (HasZvlsseg) + Features |= RISCVFeature_ZVLSSEG; + + return Features; +} + +static void InsertRVVBuiltinDeclarationsFromTable( + Sema &S, LookupResult &LR, IdentifierInfo *II, Preprocessor &PP, + unsigned FctIndex, unsigned Len, unsigned BuiltinIndex) { + + // FIXME: + // It is hacky here. If we create the implicit funciton declarations, these + // overloaded builtins will trigger "call to %0 is ambiguous" error message. + // For example, + // vint8m1_t vloxei8(const int8_t *, vuint8m1_t, size_t); + // vuint8m1_t vloxei8(const uint8_t *, vuint8m1_t, size_t); + // The first pointer type is implicitly convertible. These two declarations + // are ambiguous. + if (II->getName() == "vloxei8" || II->getName() == "vloxei16" || + II->getName() == "vloxei32" || II->getName() == "vloxei64" || + II->getName() == "vluxei8" || II->getName() == "vluxei16" || + II->getName() == "vluxei32" || II->getName() == "vluxei64") + return; + + ASTContext &Context = S.Context; + uint32_t RVVTargetFeatures = GetTargetFeatures(Context.getTargetInfo()); + + for (unsigned SignatureIndex = 0; SignatureIndex < Len; SignatureIndex++) { + const RVVBuiltinStruct &RVVBuiltin = + RVVBuiltinTable[FctIndex + SignatureIndex]; + + if ((RVVBuiltin.RequiredExts & RVVTargetFeatures) != + RVVBuiltin.RequiredExts) + continue; + + QualType RetType; + SmallVector ArgTypes; + GetQualTypesForRVVBuiltin(S, RVVBuiltin, RetType, ArgTypes); + + QualType BuiltinFuncType; + GetRVVBuiltinFuncType(Context, BuiltinFuncType, RetType, ArgTypes); + + SourceLocation Loc = LR.getNameLoc(); + DeclContext *Parent = Context.getTranslationUnitDecl(); + FunctionDecl *NewRVVBuiltin = FunctionDecl::Create( + Context, Parent, Loc, Loc, II, BuiltinFuncType, /*TInfo=*/nullptr, + SC_Extern, S.getCurFPFeatures().isFPConstrained(), false, + BuiltinFuncType->isFunctionProtoType()); + NewRVVBuiltin->setImplicit(); + + // 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, NewRVVBuiltin, SourceLocation(), SourceLocation(), nullptr, + FP->getParamType(IParm), nullptr, SC_None, nullptr); + Parm->setScopeInfo(0, IParm); + ParmList.push_back(Parm); + } + NewRVVBuiltin->setParams(ParmList); + NewRVVBuiltin->addAttr(OverloadableAttr::CreateImplicit(Context)); + // Add __clang_builtin_alias__(BuiltinName) attribute. + unsigned BuiltinID = BuiltinIndex; + // If it is a generic C builtin, find the clang builtin by + // (name, signature index). + if (Len > 1) + BuiltinID = RVVGenericBuiltinMap[std::make_pair( + II->getName(), RVVBuiltin.SigTableIndex)]; + auto &IntrinsicII = PP.getIdentifierTable().get(RVVBuiltinName[BuiltinID]); + NewRVVBuiltin->addAttr( + BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII)); + + LR.addDecl(NewRVVBuiltin); + } + + if (Len > 1) + LR.resolveKind(); +} + /// Lookup a builtin function, when name lookup would otherwise /// fail. bool Sema::LookupBuiltin(LookupResult &R) { @@ -928,6 +1051,18 @@ } } + // TODO: Find a better way to detect RVV. + if (PP.getPredefines() == "#define __riscv_pragma_vector_intrinsics") { + unsigned SigIndex, SigSize, BuiltinIndex; + std::tie(SigIndex, SigSize, BuiltinIndex) = + GetRVVBuiltinInfo(II->getName()); + if (SigIndex) { + InsertRVVBuiltinDeclarationsFromTable(*this, R, II, PP, SigIndex - 1, + SigSize, BuiltinIndex); + return true; + } + } + // If this is a builtin on this (or all) targets, create the decl. if (unsigned BuiltinID = II->getBuiltinID()) { // In C++ and OpenCL (spec v1.2 s6.9.f), we don't have any predefined 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 @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" @@ -22,6 +23,8 @@ #include "llvm/ADT/Twine.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" #include using namespace llvm; @@ -55,7 +58,6 @@ Float, Invalid, }; - BasicType BT; ScalarTypeKind ScalarType = Invalid; LMULType LMUL; bool IsPointer = false; @@ -71,11 +73,30 @@ std::string ClangBuiltinStr; std::string Str; std::string ShortStr; + std::string QualExpr; + std::string MangledStr; public: RVVType() : RVVType(BasicType(), 0, StringRef()) {} RVVType(BasicType BT, int Log2LMUL, StringRef prototype); + StringRef getMangledStr() { + if (MangledStr.empty()) { + if (!Valid) + MangledStr = "invalid"; + else { + MangledStr = + (Twine(ScalarType) + Twine(IsPointer) + Twine(IsImmediate) + + Twine(IsConstant) + Twine(ElementBitwidth)) + .str(); + if (!isScalar()) + MangledStr += (Twine(Scale.getValue()) + LMUL.str()).str(); + } + } + + return MangledStr; + } + // Return the string representation of a type, which is an encoded string for // passing to the BUILTIN() macro in Builtins.def. const std::string &getBuiltinStr() const { return BuiltinStr; } @@ -97,6 +118,8 @@ return ShortStr; } + const std::string &getQualExpr() { return QualExpr; } + bool isValid() const { return Valid; } bool isScalar() const { return Scale.hasValue() && Scale.getValue() == 0; } bool isVector() const { return Scale.hasValue() && Scale.getValue() != 0; } @@ -110,13 +133,15 @@ bool isFloat(unsigned Width) const { return isFloat() && ElementBitwidth == Width; } + bool isPointer() const { return IsPointer; } + bool isConstant() const { return IsConstant; } private: // Verify RVV vector type and set Valid. bool verifyType() const; // Creates a type based on basic types of TypeRange - void applyBasicType(); + void applyBasicType(BasicType BT); // Applies a prototype modifier to the current type. The result maybe an // invalid type. @@ -130,6 +155,8 @@ void initTypeStr(); // Compute and record a short name of a type for C/C++ name suffix. void initShortStr(); + // Compute and record the expression to create QualType. + void initQualExpr(); }; using RVVTypePtr = RVVType *; @@ -159,13 +186,12 @@ bool HasNoMaskedOverloaded; bool HasAutoDef; // There is automiatic definition in header std::string ManualCodegen; - RVVTypePtr OutputType; // Builtin output type - RVVTypes InputTypes; // Builtin input types // The types we use to obtain the specific LLVM intrinsic. They are index of - // InputTypes. -1 means the return type. + // input types. -1 means the return type. std::vector IntrinsicTypes; uint8_t RISCVExtensions = 0; unsigned NF = 1; + const RVVTypes Types; public: RVVIntrinsic(StringRef Name, StringRef Suffix, StringRef MangledName, @@ -193,6 +219,7 @@ const std::vector &getIntrinsicTypes() const { return IntrinsicTypes; } + const RVVTypes &getRVVTypes() const { return Types; } // Return the type string for a BUILTIN() macro in Builtins.def. std::string getBuiltinTypeStr() const; @@ -201,23 +228,79 @@ // init the RVVIntrinsic ID and IntrinsicTypes. void emitCodeGenSwitchBody(raw_ostream &o) const; - // Emit the macros for mapping C/C++ intrinsic function to builtin functions. - void emitIntrinsicFuncDef(raw_ostream &o) const; - // Emit the mangled function definition. void emitMangledFuncDef(raw_ostream &o) const; }; +// A list of signatures that are shared by one or more builtin functions. +struct BuiltinTableEntries { + SmallVector Names; + SmallVector BuiltinIndex; + std::vector> Signatures; +}; + class RVVEmitter { private: + // A list of indices into the builtin function table. + using BuiltinIndexListTy = SmallVector; + RecordKeeper &Records; + std::vector> Defs; std::string HeaderCode; // Concat BasicType, LMUL and Proto as key StringMap LegalTypes; StringSet<> IllegalTypes; + MapVector TypeMap; + std::vector TypeList; + std::vector BuiltinList; + // Map (generic name, RVVSignatureTable index) to RVVBuiltinName index + MapVector, unsigned> GenericBuiltinMap; + + // Contains a list of the available signatures, without the name of the + // function. Each pair consists of a signature and a cumulative index. + // E.g.: <, 0>, + // <, 4>, + // <, 10>, + std::vector> SignaturesList; + + // Map the name of a builtin function to its prototypes (instances of the + // "RVVIntrinsic" class). + // Each prototype is registered as a pair of: + // + // E.g.: The function vadd: + // vint8m1_t vadd(vint8m1_t, vint8m1_t, size_t); + // vint8m1_t vadd(vbool8_t, vint8m1_t, vint8m1_t, vint8m1_t, size_t); + // vint8m2_t vadd(vint8m2_t, vint8m2_t, size_t); + // ... + // <"vadd", <, + // , + // , + // ...>> + MapVector>> + FctOverloadMap; + + // Map an ordered vector of signatures to their original RVVIntrinsic + // instances, and to a list of function names that share these signatures. + // + // For example, suppose the "vadd" and "vsub" functions have only three + // signatures, and these signatures are at index Ix in the SignatureTable: + // vadd | vsub | Signature | Index + // vint8m1_t vadd() | vint8m1_t vsub() | Signature1 | I1 + // vint8m2_t vadd() | vint8m2_t vsub() | Signature2 | I2 + // vint8m4_t vadd() | vint8m4_t vsub() | Signature3 | I3 + // + // Then we will create a mapping of the vector of signatures: + // SignatureListMap[] = < + // <"vadd", "vsub">, + // > + // The function "vand", having the same signatures, would be mapped to the + // same entry (). + MapVector SignatureListMap; + public: - RVVEmitter(RecordKeeper &R) : Records(R) {} + RVVEmitter(RecordKeeper &R) : Records(R) { createRVVIntrinsics(Defs); } /// Emit riscv_vector.h void createHeader(raw_ostream &o); @@ -228,6 +311,9 @@ /// Emit all the information needed to map builtin -> LLVM IR intrinsic. void createCodeGen(raw_ostream &o); + /// Emit all the information needed by SemaLookup.cpp. + void createSema(raw_ostream &o); + std::string getSuffixStr(char Type, int Log2LMUL, StringRef Prototypes); private: @@ -256,6 +342,19 @@ // prototype string individually in the Handler. void parsePrototypes(StringRef Prototypes, std::function Handler); + + void EmitDeclarations(raw_ostream &o); + void GetOverloads(raw_ostream &o); + bool CanReuseSignature( + BuiltinIndexListTy *Candidate, + std::vector> &SignatureList); + void GroupBySignature(raw_ostream &OS); + + void EmitSignatureTable(raw_ostream &o); + void EmitBuiltinTable(raw_ostream &o); + void EmitBuiltinMapTable(raw_ostream &o); + void EmitStringMatcher(raw_ostream &o); + void EmitQualTypeFinder(raw_ostream &o); }; } // namespace @@ -309,13 +408,14 @@ } RVVType::RVVType(BasicType BT, int Log2LMUL, StringRef prototype) - : BT(BT), LMUL(LMULType(Log2LMUL)) { - applyBasicType(); + : LMUL(LMULType(Log2LMUL)) { + applyBasicType(BT); applyModifier(prototype); Valid = verifyType(); if (Valid) { initBuiltinStr(); initTypeStr(); + initQualExpr(); if (isVector()) { initClangBuiltinStr(); } @@ -555,6 +655,21 @@ case ScalarTypeKind::UnsignedInteger: ShortStr = "u" + utostr(ElementBitwidth); break; + case ScalarTypeKind::Void: + ShortStr = "void"; + break; + case ScalarTypeKind::Size_t: + ShortStr = "size_t"; + break; + case ScalarTypeKind::Ptrdiff_t: + ShortStr = "ptrdiff_t"; + break; + case ScalarTypeKind::UnsignedLong: + ShortStr = "ulong"; + break; + case ScalarTypeKind::SignedLong: + ShortStr = "long"; + break; default: PrintFatalError("Unhandled case!"); } @@ -562,7 +677,85 @@ ShortStr += LMUL.str(); } -void RVVType::applyBasicType() { +void RVVType::initQualExpr() { + switch (ScalarType) { + case ScalarTypeKind::Invalid: + llvm_unreachable("Type is not valid."); + case ScalarTypeKind::Void: + QualExpr = "Context.VoidTy"; + break; + case ScalarTypeKind::Size_t: + QualExpr = "Context.getSizeType()"; + break; + case ScalarTypeKind::Ptrdiff_t: + QualExpr = "Context.getPointerDiffType()"; + break; + case ScalarTypeKind::UnsignedLong: + QualExpr = "Context.UnsignedLongTy"; + break; + case ScalarTypeKind::SignedLong: + QualExpr = "Context.LongTy"; + break; + case ScalarTypeKind::Boolean: + QualExpr = "Context.BoolTy"; + break; + case ScalarTypeKind::SignedInteger: + switch (ElementBitwidth) { + case 8: + QualExpr = "Context.SignedCharTy"; + break; + case 16: + QualExpr = "Context.ShortTy"; + break; + case 32: + QualExpr = "Context.IntTy"; + break; + case 64: + QualExpr = "Context.LongLongTy"; + break; + } + break; + case ScalarTypeKind::UnsignedInteger: + switch (ElementBitwidth) { + case 8: + QualExpr = "Context.UnsignedCharTy"; + break; + case 16: + QualExpr = "Context.UnsignedShortTy"; + break; + case 32: + QualExpr = "Context.UnsignedIntTy"; + break; + case 64: + QualExpr = "Context.UnsignedLongLongTy"; + break; + } + break; + case ScalarTypeKind::Float: + switch (ElementBitwidth) { + case 16: + QualExpr = "Context.Float16Ty"; + break; + case 32: + QualExpr = "Context.FloatTy"; + break; + case 64: + QualExpr = "Context.DoubleTy"; + break; + } + break; + } + + if (isScalar()) + return; + + assert(isVector() && "Unexpected type."); + QualExpr = Twine("Context.getScalableVectorType(" + QualExpr + ", " + + Twine(Scale.getValue()) + ")") + .str(); +} + +void RVVType::applyBasicType(BasicType BT) { switch (BT) { case 'c': ElementBitwidth = 8; @@ -733,6 +926,7 @@ break; case 'K': IsImmediate = true; + IsConstant = true; break; case 'U': ScalarType = ScalarTypeKind::UnsignedInteger; @@ -767,7 +961,7 @@ StringRef RequiredExtension, unsigned NF) : IRName(IRName), IsMask(IsMask), HasVL(HasVL), HasPolicy(HasPolicy), HasNoMaskedOverloaded(HasNoMaskedOverloaded), HasAutoDef(HasAutoDef), - ManualCodegen(ManualCodegen.str()), NF(NF) { + ManualCodegen(ManualCodegen.str()), NF(NF), Types(OutInTypes) { // Init BuiltinName, Name and MangledName BuiltinName = NewName.str(); @@ -797,10 +991,6 @@ if (RequiredExtension == "Zvlsseg") RISCVExtensions |= RISCVExtension::Zvlsseg; - // Init OutputType and InputTypes - OutputType = OutInTypes[0]; - InputTypes.assign(OutInTypes.begin() + 1, OutInTypes.end()); - // IntrinsicTypes is nonmasked version index. Need to update it // if there is maskedoff operand (It is always in first operand). IntrinsicTypes = NewIntrinsicTypes; @@ -814,10 +1004,9 @@ std::string RVVIntrinsic::getBuiltinTypeStr() const { std::string S; - S += OutputType->getBuiltinStr(); - for (const auto &T : InputTypes) { - S += T->getBuiltinStr(); - } + S += Types[0]->getBuiltinStr(); + for (unsigned i = 1; i < Types.size(); ++i) + S += Types[i]->getBuiltinStr(); return S; } @@ -860,30 +1049,30 @@ OS << " break;\n"; } -void RVVIntrinsic::emitIntrinsicFuncDef(raw_ostream &OS) const { - OS << "__attribute__((__clang_builtin_alias__("; - OS << "__builtin_rvv_" << getBuiltinName() << ")))\n"; - OS << OutputType->getTypeStr() << " " << getName() << "("; - // Emit function arguments - if (!InputTypes.empty()) { - ListSeparator LS; - for (unsigned i = 0; i < InputTypes.size(); ++i) - OS << LS << InputTypes[i]->getTypeStr(); - } - OS << ");\n"; -} - void RVVIntrinsic::emitMangledFuncDef(raw_ostream &OS) const { - OS << "__attribute__((__clang_builtin_alias__("; - OS << "__builtin_rvv_" << getBuiltinName() << ")))\n"; - OS << OutputType->getTypeStr() << " " << getMangledName() << "("; - // Emit function arguments - if (!InputTypes.empty()) { - ListSeparator LS; - for (unsigned i = 0; i < InputTypes.size(); ++i) - OS << LS << InputTypes[i]->getTypeStr(); + // FIXME: + // It is hacky here. If we create the implicit funciton declarations, these + // overloaded builtins will trigger "call to %0 is ambiguous" error message. + // For example, + // vint8m1_t vloxei8(const int8_t *, vuint8m1_t, size_t); + // vuint8m1_t vloxei8(const uint8_t *, vuint8m1_t, size_t); + // The first pointer type is implicitly convertible. These two declarations + // are ambiguous. + if (getMangledName() == "vloxei8" || getMangledName() == "vloxei16" || + getMangledName() == "vloxei32" || getMangledName() == "vloxei64" || + getMangledName() == "vluxei8" || getMangledName() == "vluxei16" || + getMangledName() == "vluxei32" || getMangledName() == "vluxei64") { + OS << "__rvv_aio __attribute__((__clang_builtin_alias__("; + OS << "__builtin_rvv_" << getBuiltinName() << ")))\n"; + OS << Types[0]->getTypeStr() << " " << getMangledName() << "("; + // Emit function arguments + if (Types.size() > 1) { + ListSeparator LS; + for (unsigned i = 1; i < Types.size(); ++i) + OS << LS << Types[i]->getTypeStr(); + } + OS << ");\n"; } - OS << ");\n"; } //===----------------------------------------------------------------------===// @@ -917,12 +1106,10 @@ OS << "#ifdef __cplusplus\n"; OS << "extern \"C\" {\n"; OS << "#endif\n\n"; + OS << "#pragma clang riscv intrinsic vector\n\n"; createRVVHeaders(OS); - std::vector> Defs; - createRVVIntrinsics(Defs); - // Print header code if (!HeaderCode.empty()) { OS << HeaderCode; @@ -981,16 +1168,6 @@ return A->getRISCVExtensions() < B->getRISCVExtensions(); }); - OS << "#define __rvv_ai static __inline__\n"; - - // Print intrinsic functions with macro - emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { - OS << "__rvv_ai "; - Inst.emitIntrinsicFuncDef(OS); - }); - - OS << "#undef __rvv_ai\n\n"; - OS << "#define __riscv_v_intrinsic_overloading 1\n"; // Print Overloaded APIs @@ -1000,7 +1177,6 @@ emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { if (!Inst.isMask() && !Inst.hasNoMaskedOverloaded()) return; - OS << "__rvv_aio "; Inst.emitMangledFuncDef(OS); }); @@ -1013,9 +1189,6 @@ } void RVVEmitter::createBuiltins(raw_ostream &OS) { - std::vector> Defs; - createRVVIntrinsics(Defs); - // Map to keep track of which builtin names have already been emitted. StringMap BuiltinMap; @@ -1046,8 +1219,6 @@ } void RVVEmitter::createCodeGen(raw_ostream &OS) { - std::vector> Defs; - createRVVIntrinsics(Defs); // IR name could be empty, use the stable sort preserves the relative order. llvm::stable_sort(Defs, [](const std::unique_ptr &A, const std::unique_ptr &B) { @@ -1254,18 +1425,17 @@ Optional RVVEmitter::computeType(BasicType BT, int Log2LMUL, StringRef Proto) { - std::string Idx = Twine(Twine(BT) + Twine(Log2LMUL) + Proto).str(); - // Search first + RVVType T(BT, Log2LMUL, Proto); + StringRef Idx = T.getMangledStr(); auto It = LegalTypes.find(Idx); if (It != LegalTypes.end()) return &(It->second); if (IllegalTypes.count(Idx)) return llvm::None; - // Compute type and record the result. - RVVType T(BT, Log2LMUL, Proto); if (T.isValid()) { // Record legal type index and value. LegalTypes.insert({Idx, T}); + TypeList.push_back(&(LegalTypes[Idx])); return &(LegalTypes[Idx]); } // Record illegal type index. @@ -1310,6 +1480,348 @@ return true; } +void RVVEmitter::EmitDeclarations(raw_ostream &OS) { + // Structure definitions. + OS << R"( +enum RISCVFeatures { + RISCVFeature_F = 1 << 1, + RISCVFeature_D = 1 << 2, + RISCVFeature_ZFH = 1 << 3, + RISCVFeature_ZVLSSEG = 1 << 4, +}; + +// One overload of an RVV builtin function. +struct RVVBuiltinStruct { + // Index of the signature in the RVVSignatureTable table. + const unsigned SigTableIndex; + // Entries between index SigTableIndex and (SigTableIndex + NumTypes - 1) in + // the RVVSignatureTable represent the complete signature. The first type at + // index SigTableIndex is the return type. + const unsigned NumTypes; + // Required extensions for the builtin. + const uint32_t RequiredExts; +}; + +)"; + + unsigned I = 0; + for (auto *Ty : TypeList) + TypeMap.insert(std::make_pair(Ty, I++)); + + // Enum of scalar/vector type names (float, int, ...). + OS << "enum RVVTypeID {\n"; + StringSet<> Seen; + for (const auto &T : TypeMap) { + auto *RVVTy = T.first; + if (Seen.insert(RVVTy->getShortStr()).second) + OS << " RVVT_" + RVVTy->getShortStr() << ",\n"; + } + OS << "};\n\n"; + + OS << R"( +// Represents a return type or argument type. +struct RVVTypeStruct { + // A type (e.g. float, int, ...). + const RVVTypeID ID; + // 0 if the type is not a pointer. + const bool IsPointer : 1; + // 0 if the type is not const. + const bool IsConst : 1; +}; + +)"; + + OS << "static const RVVTypeStruct RVVTypeTable[] = {\n"; + for (const auto &T : TypeMap) { + auto *RVVTy = T.first; + OS << " // " << T.second << ", " << RVVTy->getTypeStr() << "\n" + << " {RVVT_" << RVVTy->getShortStr() << ", " << RVVTy->isPointer() + << ", " << RVVTy->isConstant() << "},\n"; + } + OS << "};\n\n"; +} + +void RVVEmitter::GetOverloads(raw_ostream &OS) { + // Populate the SignaturesList and the FctOverloadMap. + unsigned CumulativeSignIndex = 0; + for (auto &Def : Defs) { + StringRef IName = Def->getName(); + auto Signature = Def->getRVVTypes(); + auto it = llvm::find_if(SignaturesList, + [&](const std::pair &a) { + return a.first == Signature; + }); + unsigned SignIndex; + if (it == SignaturesList.end()) { + SignaturesList.push_back(std::make_pair(Signature, CumulativeSignIndex)); + SignIndex = CumulativeSignIndex; + CumulativeSignIndex += Signature.size(); + } else { + SignIndex = it->second; + } + FctOverloadMap[IName].push_back(std::make_pair(Def.get(), SignIndex)); + + if (Def->isMask() || Def->hasNoMaskedOverloaded()) { + // Put the generic builtin into FctOverloadMap. + // For example, vadd_vv_i8m1 has 'vadd' generic builtin. + StringRef GName = Def->getMangledName(); + FctOverloadMap[GName].push_back(std::make_pair(Def.get(), SignIndex)); + } + + auto builtin_it = llvm::find_if(BuiltinList, [&](const std::string &a) { + return a == "__builtin_rvv_" + Def->getBuiltinName().str(); + }); + unsigned BuiltinIdx = builtin_it - BuiltinList.begin(); + if (Def->isMask() || Def->hasNoMaskedOverloaded()) { + StringRef GName = Def->getMangledName(); + GenericBuiltinMap.insert( + std::make_pair(std::make_pair(GName, SignIndex), BuiltinIdx)); + } + + if (builtin_it == BuiltinList.end()) { + BuiltinList.push_back("__builtin_rvv_" + Def->getBuiltinName().str()); + } + } +} + +bool RVVEmitter::CanReuseSignature( + BuiltinIndexListTy *Candidate, + std::vector> &SignatureList) { + assert(Candidate->size() == SignatureList.size() && + "signature lists should have the same size"); + + auto &CandidateSigs = SignatureListMap.find(Candidate)->second.Signatures; + for (unsigned Index = 0; Index < Candidate->size(); Index++) { + auto *Rec = SignatureList[Index].first; + auto *Rec2 = CandidateSigs[Index].first; + if (Rec->getRISCVExtensions() == Rec2->getRISCVExtensions()) + return true; + } + return false; +} + +void RVVEmitter::GroupBySignature(raw_ostream &OS) { + // List of signatures known to be emitted. + std::vector KnownSignatures; + + for (auto &Fct : FctOverloadMap) { + bool FoundReusableSig = false; + + // Gather all signatures for the current function. + auto *CurSignatureList = new BuiltinIndexListTy(); + for (const auto &Signature : Fct.second) + CurSignatureList->push_back(Signature.second); + + // Sort the list to facilitate future comparisons. + llvm::sort(*CurSignatureList); + + auto it = llvm::find_if(BuiltinList, [&](const std::string &a) { + return a == + "__builtin_rvv_" + Fct.second[0].first->getBuiltinName().str(); + }); + unsigned BuiltinIndex = it - BuiltinList.begin(); + // Check if we have already seen another function with the same list of + // signatures. If so, just add the name of the function. + for (auto *Candidate : KnownSignatures) { + if (Candidate->size() == CurSignatureList->size() && + *Candidate == *CurSignatureList && + CanReuseSignature(Candidate, Fct.second)) { + auto &BuiltinTableEntry = SignatureListMap.find(Candidate)->second; + BuiltinTableEntry.Names.push_back(Fct.first); + BuiltinTableEntry.BuiltinIndex.push_back(BuiltinIndex); + FoundReusableSig = true; + } + } + + if (FoundReusableSig) { + delete CurSignatureList; + } else { + // Add a new entry. + SignatureListMap[CurSignatureList] = { + SmallVector(1, Fct.first), + SmallVector(1, BuiltinIndex), Fct.second}; + KnownSignatures.push_back(CurSignatureList); + } + } + + for (auto *I : KnownSignatures) + delete I; +} + +void RVVEmitter::EmitSignatureTable(raw_ostream &OS) { + // The type is stored as an index + // of RVVTypeTable. Multiple entries following each other form a + // signature. + OS << "static const uint16_t RVVSignatureTable[] = {\n"; + for (const auto &P : SignaturesList) { + OS << " // " << P.second << ", "; + for (auto *Ty : P.first) + OS << Ty->getTypeStr() << " "; + OS << "\n"; + for (auto *Ty : P.first) { + unsigned Entry = TypeMap.find(Ty)->second; + if (Entry > USHRT_MAX) { + // Report an error when seeing an entry that is too large for the + // current index type (uint16_t). When hitting this, the type + // of SignatureTable will need to be changed. + PrintFatalError("Entry in SignatureTable exceeds limit."); + } + OS << Entry << ", "; + } + OS << "\n"; + } + OS << "};\n\n"; +} + +void RVVEmitter::EmitBuiltinTable(raw_ostream &OS) { + OS << "static const char *RVVBuiltinName[] = {\n"; + for (const auto &B : BuiltinList) + OS << " \"" << B << "\",\n"; + OS << "};\n\n"; + + unsigned Index = 0; + OS << "static const RVVBuiltinStruct RVVBuiltinTable[] = {\n"; + for (const auto &SLM : SignatureListMap) { + + OS << " // " << (Index + 1) << ": "; + for (const auto &Name : SLM.second.Names) { + OS << Name << ", "; + } + OS << "\n"; + + OS << " // Builtins: "; + for (const auto &BuiltinIndex : SLM.second.BuiltinIndex) { + OS << BuiltinList[BuiltinIndex] << ", "; + } + OS << "\n"; + + for (const auto &Overload : SLM.second.Signatures) { + RVVIntrinsic *Intr = Overload.first; + OS << " {" << Overload.second << ", " << Intr->getRVVTypes().size() + << ", " << +Intr->getRISCVExtensions() << "},\n"; + Index++; + } + } + OS << "};\n"; +} + +void RVVEmitter::EmitBuiltinMapTable(raw_ostream &OS) { + OS << "static std::map, unsigned> "; + OS << " RVVGenericBuiltinMap = {\n"; + for (const auto &M : GenericBuiltinMap) { + OS << " // " << BuiltinList[M.second] << "\n"; + OS << " {std::make_pair(\"" << M.first.first << "\", " << M.first.second + << "), "; + OS << M.second << "},\n"; + } + OS << "};\n"; +} + +void RVVEmitter::EmitStringMatcher(raw_ostream &OS) { + std::vector ValidBuiltins; + unsigned CumulativeIndex = 1; + + for (const auto &SLM : SignatureListMap) { + const auto &Ovl = SLM.second.Signatures; + + // A single signature list may be used by different builtins. Return the + // same pair for each of those builtins. + for (unsigned i = 0; i < SLM.second.Names.size(); ++i) { + const auto &FctName = SLM.second.Names[i]; + unsigned BuiltinIndex = SLM.second.BuiltinIndex[i]; + std::string RetStmt; + raw_string_ostream SS(RetStmt); + SS << "return std::make_tuple(" << CumulativeIndex << ", " << Ovl.size() + << ", " << BuiltinIndex << ");"; + SS.flush(); + ValidBuiltins.push_back( + StringMatcher::StringPair(std::string(FctName), RetStmt)); + } + CumulativeIndex += Ovl.size(); + } + + OS << R"( +// Find out whether a string matches an existing RVV builtin function name. +// Returns: A tuple <0, 0, 0> if no name matches. +// A tuple indexing the RVVBuiltinTable if the name is +// matching an RVV builtin function. +static std::tuple GetRVVBuiltinInfo(llvm::StringRef Name) { + +)"; + + StringMatcher("Name", ValidBuiltins, OS).Emit(0, true); + + OS << " return std::make_tuple(0, 0, 0);\n"; + OS << "} // GetRVVBuiltinInfo\n"; +} + +void RVVEmitter::EmitQualTypeFinder(raw_ostream &OS) { + OS << R"( + +// Convert a RVVTypeStruct type to a QualTypes. +static void RVV2Qual(Sema &S, const RVVTypeStruct &Ty, QualType &QT) { + ASTContext &Context = S.Context; +)"; + + // Step 1. + // Start of switch statement over all types. + OS << "\n switch (Ty.ID) {\n"; + + // Only insert the plain scalar type; vector information and type qualifiers + // are added in step 2. + StringSet<> TypesSeen; + for (const auto &T : TypeMap) { + auto *RVVTy = T.first; + if (TypesSeen.insert(RVVTy->getShortStr()).second) { + // Get QualType + OS << " case RVVT_" << RVVTy->getShortStr() << ":\n"; + OS << " QT = " << RVVTy->getQualExpr() << ";\n"; + OS << " break;\n"; + } + } + // End of switch statement. + OS << " } // end of switch (Ty.ID)\n\n"; + + // Step 2. + // Assign the right attributes to the types. + OS << R"( + if (Ty.IsConst != 0) { + QT = Context.getConstType(QT); + } + + // Transform the type to a pointer as the last step, if necessary. + if (Ty.IsPointer != 0) { + QT = Context.getPointerType(QT); + } +)"; + + // End of the "RVV2Qual" function. + OS << "\n} // RVV2Qual\n"; +} + +void RVVEmitter::createSema(raw_ostream &OS) { + emitSourceFileHeader("RISC-V Vector Builtin handling", OS); + + OS << "#include \"llvm/ADT/StringRef.h\"\n"; + OS << "using namespace clang;\n\n"; + + // Emit enums and structs. + EmitDeclarations(OS); + + // Parse the Records to populate the internal lists. + GetOverloads(OS); + GroupBySignature(OS); + + // Emit tables. + EmitSignatureTable(OS); + EmitBuiltinTable(OS); + EmitBuiltinMapTable(OS); + + // Emit functions. + EmitStringMatcher(OS); + EmitQualTypeFinder(OS); +} + namespace clang { void EmitRVVHeader(RecordKeeper &Records, raw_ostream &OS) { RVVEmitter(Records).createHeader(OS); @@ -1323,4 +1835,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); diff --git a/llvm/docs/CommandGuide/tblgen.rst b/llvm/docs/CommandGuide/tblgen.rst --- a/llvm/docs/CommandGuide/tblgen.rst +++ b/llvm/docs/CommandGuide/tblgen.rst @@ -348,6 +348,10 @@ Generate ``riscv_vector_builtin_cg.inc`` for Clang. +.. option:: -gen-riscv-vector-builtin-sema + + Generate ``riscv_vector_builtin_sema.inc`` for Clang. + .. option:: -gen-attr-docs Generate attribute documentation.