//===- RISCVVEmitter.cpp - Generate riscv_vector.h for use with clang -----===// // // 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 tablegen backend is responsible for emitting riscv_vector.h which // includes a declaration and definition of each intrinsic functions specified // in https://github.com/riscv/rvv-intrinsic-doc. // // See also the documentation in include/clang/Basic/riscv_vector.td. // //===----------------------------------------------------------------------===// #include "clang/Support/RISCVVIntrinsicUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallSet.h" #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" #include #include using namespace llvm; 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. uint32_t RequiredExtensions; // Prototype for this intrinsic. SmallVector Prototype; // 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; bool HasMasked :1; bool HasVL :1; bool HasMaskedOffOperand :1; bool HasTailPolicy : 1; bool HasMaskPolicy : 1; bool HasFRMRoundModeOp : 1; bool IsTuple : 1; uint8_t UnMaskedPolicyScheme : 2; uint8_t MaskedPolicyScheme : 2; }; // Compressed function signature table. class SemaSignatureTable { private: std::vector SignatureTable; void insert(ArrayRef Signature); public: static constexpr unsigned INVALID_INDEX = ~0U; // Create compressed signature table from SemaRecords. void init(ArrayRef SemaRecords); // Query the Signature, return INVALID_INDEX if not found. unsigned getIndex(ArrayRef Signature); /// Print signature table in RVVHeader Record to \p OS void print(raw_ostream &OS); }; class RVVEmitter { private: RecordKeeper &Records; RVVTypeCache TypeCache; public: RVVEmitter(RecordKeeper &R) : Records(R) {} /// Emit riscv_vector.h void createHeader(raw_ostream &o); /// Emit all the __builtin prototypes and code needed by Sema. void createBuiltins(raw_ostream &o); /// Emit all the information needed to map builtin -> LLVM IR intrinsic. void createCodeGen(raw_ostream &o); /// Emit all the information needed by SemaRISCVVectorLookup.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 and SemaRecords. void createRVVIntrinsics(std::vector> &Out, std::vector *SemaRecords = nullptr); /// Create all intrinsic records and SemaSignatureTable from SemaRecords. void createRVVIntrinsicRecords(std::vector &Out, SemaSignatureTable &SST, ArrayRef SemaRecords); /// Print HeaderCode in RVVHeader Record to \p Out void printHeaderCode(raw_ostream &OS); }; } // namespace static BasicType ParseBasicType(char c) { switch (c) { case 'c': return BasicType::Int8; break; case 's': return BasicType::Int16; break; case 'i': return BasicType::Int32; break; case 'l': return BasicType::Int64; break; case 'x': return BasicType::Float16; break; case 'f': return BasicType::Float32; break; case 'd': return BasicType::Float64; break; case 'y': return BasicType::BFloat16; break; default: return BasicType::Unknown; } } static VectorTypeModifier getTupleVTM(unsigned NF) { assert(2 <= NF && NF <= 8 && "2 <= NF <= 8"); return static_cast( static_cast(VectorTypeModifier::Tuple2) + (NF - 2)); } void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) { if (!RVVI->getIRName().empty()) OS << " ID = Intrinsic::riscv_" + RVVI->getIRName() + ";\n"; if (RVVI->getNF() >= 2) OS << " NF = " + utostr(RVVI->getNF()) + ";\n"; OS << " PolicyAttrs = " << RVVI->getPolicyAttrsBits() << ";\n"; if (RVVI->hasManualCodegen()) { OS << "IsMasked = " << (RVVI->isMasked() ? "true" : "false") << ";\n"; OS << RVVI->getManualCodegen(); OS << "break;\n"; return; } for (const auto &I : enumerate(RVVI->getInputTypes())) { if (I.value()->isPointer()) { assert(RVVI->getIntrinsicTypes().front() == -1 && "RVVI should be vector load intrinsic."); } } if (RVVI->isMasked()) { if (RVVI->hasVL()) { OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n"; if (RVVI->hasPolicyOperand()) OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType()," " PolicyAttrs));\n"; if (RVVI->hasMaskedOffOperand() && RVVI->getPolicyAttrs().isTAMAPolicy()) OS << " Ops.insert(Ops.begin(), " "llvm::PoisonValue::get(ResultType));\n"; // Masked reduction cases. if (!RVVI->hasMaskedOffOperand() && RVVI->hasPassthruOperand() && RVVI->getPolicyAttrs().isTAMAPolicy()) OS << " Ops.insert(Ops.begin(), " "llvm::PoisonValue::get(ResultType));\n"; } else { OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n"; } } else { if (RVVI->hasPolicyOperand()) OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), " "PolicyAttrs));\n"; else if (RVVI->hasPassthruOperand() && RVVI->getPolicyAttrs().isTAPolicy()) OS << " Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));\n"; } OS << " IntrinsicTypes = {"; ListSeparator LS; for (const auto &Idx : RVVI->getIntrinsicTypes()) { if (Idx == -1) OS << LS << "ResultType"; else OS << LS << "Ops[" << Idx << "]->getType()"; } // VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is // always last operand. if (RVVI->hasVL()) OS << ", Ops.back()->getType()"; OS << "};\n"; OS << " break;\n"; } //===----------------------------------------------------------------------===// // SemaSignatureTable implementation //===----------------------------------------------------------------------===// void SemaSignatureTable::init(ArrayRef SemaRecords) { // 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) const { 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); }; assert(!SemaRecords.empty()); for (const SemaRecord &SR : SemaRecords) { InsertToSignatureSet(SR.Prototype); InsertToSignatureSet(SR.Suffix); InsertToSignatureSet(SR.OverloadedSuffix); } for (auto &Sig : Signatures) insert(Sig); } void SemaSignatureTable::insert(ArrayRef Signature) { if (getIndex(Signature) != INVALID_INDEX) return; // Insert Signature into SignatureTable if not found in the table. SignatureTable.insert(SignatureTable.begin(), Signature.begin(), Signature.end()); } unsigned SemaSignatureTable::getIndex(ArrayRef Signature) { // Empty signature could be point into any index since there is length // field when we use, so just always point it to 0. if (Signature.empty()) return 0; // Checking Signature already in table or not. if (Signature.size() <= SignatureTable.size()) { size_t Bound = SignatureTable.size() - Signature.size() + 1; for (size_t Index = 0; Index < Bound; ++Index) { if (equal(Signature.begin(), Signature.end(), SignatureTable.begin() + Index)) return Index; } } return INVALID_INDEX; } void SemaSignatureTable::print(raw_ostream &OS) { for (const auto &Sig : SignatureTable) OS << "PrototypeDescriptor(" << static_cast(Sig.PT) << ", " << static_cast(Sig.VTM) << ", " << static_cast(Sig.TM) << "),\n"; } //===----------------------------------------------------------------------===// // RVVEmitter implementation //===----------------------------------------------------------------------===// void RVVEmitter::createHeader(raw_ostream &OS) { OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics " "-------------------===\n" " *\n" " *\n" " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " "Exceptions.\n" " * See https://llvm.org/LICENSE.txt for license information.\n" " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" " *\n" " *===-----------------------------------------------------------------" "------===\n" " */\n\n"; OS << "#ifndef __RISCV_VECTOR_H\n"; OS << "#define __RISCV_VECTOR_H\n\n"; OS << "#include \n"; OS << "#include \n\n"; OS << "#ifndef __riscv_vector\n"; OS << "#error \"Vector intrinsics require the vector extension.\"\n"; OS << "#endif\n\n"; OS << "#ifdef __cplusplus\n"; OS << "extern \"C\" {\n"; OS << "#endif\n\n"; OS << "#pragma clang riscv intrinsic vector\n\n"; printHeaderCode(OS); auto printType = [&](auto T) { OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr() << ";\n"; }; constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3}; // Print RVV boolean types. for (int Log2LMUL : Log2LMULs) { auto T = TypeCache.computeType(BasicType::Int8, Log2LMUL, PrototypeDescriptor::Mask); if (T) printType(*T); } // Print RVV int/float types. for (char I : StringRef("csil")) { BasicType BT = ParseBasicType(I); for (int Log2LMUL : Log2LMULs) { auto T = TypeCache.computeType(BT, Log2LMUL, PrototypeDescriptor::Vector); if (T) { printType(*T); auto UT = TypeCache.computeType( BT, Log2LMUL, PrototypeDescriptor(BaseTypeModifier::Vector, VectorTypeModifier::NoModifier, TypeModifier::UnsignedInteger)); printType(*UT); } for (int NF = 2; NF <= 8; ++NF) { auto TupleT = TypeCache.computeType( BT, Log2LMUL, PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), TypeModifier::SignedInteger)); auto TupleUT = TypeCache.computeType( BT, Log2LMUL, PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), TypeModifier::UnsignedInteger)); if (TupleT) printType(*TupleT); if (TupleUT) printType(*TupleUT); } } } for (BasicType BT : {BasicType::Float16, BasicType::Float32, BasicType::Float64, BasicType::BFloat16}) { for (int Log2LMUL : Log2LMULs) { auto T = TypeCache.computeType(BT, Log2LMUL, PrototypeDescriptor::Vector); if (T) printType(*T); for (int NF = 2; NF <= 8; ++NF) { auto TupleT = TypeCache.computeType( BT, Log2LMUL, PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), (BT == BasicType::BFloat16 ? TypeModifier::BFloat : TypeModifier::Float))); if (TupleT) printType(*TupleT); } } } OS << "#define __riscv_v_intrinsic_overloading 1\n"; OS << "\n#ifdef __cplusplus\n"; OS << "}\n"; OS << "#endif // __cplusplus\n"; OS << "#endif // __RISCV_VECTOR_H\n"; } 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; OS << "#if defined(TARGET_BUILTIN) && !defined(RISCVV_BUILTIN)\n"; OS << "#define RISCVV_BUILTIN(ID, TYPE, ATTRS) TARGET_BUILTIN(ID, TYPE, " "ATTRS, \"zve32x\")\n"; OS << "#endif\n"; for (auto &Def : Defs) { auto P = BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get())); if (!P.second) { // Verf that this would have produced the same builtin definition. if (P.first->second->hasBuiltinAlias() != Def->hasBuiltinAlias()) PrintFatalError("Builtin with same name has different hasAutoDef"); else if (!Def->hasBuiltinAlias() && P.first->second->getBuiltinTypeStr() != Def->getBuiltinTypeStr()) PrintFatalError("Builtin with same name has different type string"); continue; } OS << "RISCVV_BUILTIN(__builtin_rvv_" << Def->getBuiltinName() << ",\""; if (!Def->hasBuiltinAlias()) OS << Def->getBuiltinTypeStr(); OS << "\", \"n\")\n"; } OS << "#undef RISCVV_BUILTIN\n"; } 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) { if (A->getIRName() == B->getIRName()) return (A->getPolicyAttrs() < B->getPolicyAttrs()); return (A->getIRName() < B->getIRName()); }); // Map to keep track of which builtin names have already been emitted. StringMap BuiltinMap; // Print switch body when the ir name, ManualCodegen or policy changes from // previous iteration. RVVIntrinsic *PrevDef = Defs.begin()->get(); for (auto &Def : Defs) { StringRef CurIRName = Def->getIRName(); if (CurIRName != PrevDef->getIRName() || (Def->getManualCodegen() != PrevDef->getManualCodegen()) || (Def->getPolicyAttrs() != PrevDef->getPolicyAttrs())) { emitCodeGenSwitchBody(PrevDef, OS); } PrevDef = Def.get(); auto P = BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get())); if (P.second) { OS << "case RISCVVector::BI__builtin_rvv_" << Def->getBuiltinName() << ":\n"; continue; } if (P.first->second->getIRName() != Def->getIRName()) PrintFatalError("Builtin with same name has different IRName"); else if (P.first->second->getManualCodegen() != Def->getManualCodegen()) PrintFatalError("Builtin with same name has different ManualCodegen"); else if (P.first->second->isMasked() != Def->isMasked()) PrintFatalError("Builtin with same name has different isMasked"); else if (P.first->second->hasVL() != Def->hasVL()) PrintFatalError("Builtin with same name has different hasVL"); else if (P.first->second->getPolicyScheme() != Def->getPolicyScheme()) PrintFatalError("Builtin with same name has different getPolicyScheme"); else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes()) PrintFatalError("Builtin with same name has different IntrinsicTypes"); } emitCodeGenSwitchBody(Defs.back().get(), OS); OS << "\n"; } void RVVEmitter::createRVVIntrinsics( std::vector> &Out, std::vector *SemaRecords) { std::vector RV = Records.getAllDerivedDefinitions("RVVBuiltin"); for (auto *R : RV) { StringRef Name = R->getValueAsString("Name"); StringRef SuffixProto = R->getValueAsString("Suffix"); StringRef OverloadedName = R->getValueAsString("OverloadedName"); StringRef OverloadedSuffixProto = R->getValueAsString("OverloadedSuffix"); StringRef Prototypes = R->getValueAsString("Prototype"); StringRef TypeRange = R->getValueAsString("TypeRange"); bool HasMasked = R->getValueAsBit("HasMasked"); bool HasMaskedOffOperand = R->getValueAsBit("HasMaskedOffOperand"); bool HasVL = R->getValueAsBit("HasVL"); Record *MPSRecord = R->getValueAsDef("MaskedPolicyScheme"); auto MaskedPolicyScheme = static_cast(MPSRecord->getValueAsInt("Value")); Record *UMPSRecord = R->getValueAsDef("UnMaskedPolicyScheme"); auto UnMaskedPolicyScheme = static_cast(UMPSRecord->getValueAsInt("Value")); std::vector Log2LMULList = R->getValueAsListOfInts("Log2LMUL"); bool HasTailPolicy = R->getValueAsBit("HasTailPolicy"); bool HasMaskPolicy = R->getValueAsBit("HasMaskPolicy"); bool SupportOverloading = R->getValueAsBit("SupportOverloading"); bool HasBuiltinAlias = R->getValueAsBit("HasBuiltinAlias"); StringRef ManualCodegen = R->getValueAsString("ManualCodegen"); std::vector IntrinsicTypes = R->getValueAsListOfInts("IntrinsicTypes"); std::vector RequiredFeatures = R->getValueAsListOfStrings("RequiredFeatures"); StringRef IRName = R->getValueAsString("IRName"); StringRef MaskedIRName = R->getValueAsString("MaskedIRName"); unsigned NF = R->getValueAsInt("NF"); bool IsTuple = R->getValueAsBit("IsTuple"); bool HasFRMRoundModeOp = R->getValueAsBit("HasFRMRoundModeOp"); const Policy DefaultPolicy; SmallVector SupportedUnMaskedPolicies = RVVIntrinsic::getSupportedUnMaskedPolicies(); SmallVector SupportedMaskedPolicies = RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy, HasMaskPolicy); // Parse prototype and create a list of primitive type with transformers // (operand) in Prototype. Prototype[0] is output operand. SmallVector BasicPrototype = parsePrototypes(Prototypes); SmallVector SuffixDesc = parsePrototypes(SuffixProto); SmallVector OverloadedSuffixDesc = parsePrototypes(OverloadedSuffixProto); // Compute Builtin types auto Prototype = RVVIntrinsic::computeBuiltinTypes( BasicPrototype, /*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL, NF, UnMaskedPolicyScheme, DefaultPolicy, IsTuple); llvm::SmallVector MaskedPrototype; if (HasMasked) MaskedPrototype = RVVIntrinsic::computeBuiltinTypes( BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, NF, MaskedPolicyScheme, DefaultPolicy, IsTuple); // Create Intrinsics for each type and LMUL. for (char I : TypeRange) { for (int Log2LMUL : Log2LMULList) { BasicType BT = ParseBasicType(I); std::optional Types = TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype); // Ignored to create new intrinsic if there are any illegal types. if (!Types) continue; auto SuffixStr = RVVIntrinsic::getSuffixStr(TypeCache, BT, Log2LMUL, SuffixDesc); auto OverloadedSuffixStr = RVVIntrinsic::getSuffixStr( TypeCache, BT, Log2LMUL, OverloadedSuffixDesc); // Create a unmasked intrinsic Out.push_back(std::make_unique( Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, /*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL, UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, ManualCodegen, *Types, IntrinsicTypes, RequiredFeatures, NF, DefaultPolicy, HasFRMRoundModeOp)); if (UnMaskedPolicyScheme != PolicyScheme::SchemeNone) for (auto P : SupportedUnMaskedPolicies) { SmallVector PolicyPrototype = RVVIntrinsic::computeBuiltinTypes( BasicPrototype, /*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL, NF, UnMaskedPolicyScheme, P, IsTuple); std::optional PolicyTypes = TypeCache.computeTypes(BT, Log2LMUL, NF, PolicyPrototype); Out.push_back(std::make_unique( Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, /*IsMask=*/false, /*HasMaskedOffOperand=*/false, HasVL, UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, ManualCodegen, *PolicyTypes, IntrinsicTypes, RequiredFeatures, NF, P, HasFRMRoundModeOp)); } if (!HasMasked) continue; // Create a masked intrinsic std::optional MaskTypes = TypeCache.computeTypes(BT, Log2LMUL, NF, MaskedPrototype); Out.push_back(std::make_unique( Name, SuffixStr, OverloadedName, OverloadedSuffixStr, MaskedIRName, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, ManualCodegen, *MaskTypes, IntrinsicTypes, RequiredFeatures, NF, DefaultPolicy, HasFRMRoundModeOp)); if (MaskedPolicyScheme == PolicyScheme::SchemeNone) continue; for (auto P : SupportedMaskedPolicies) { SmallVector PolicyPrototype = RVVIntrinsic::computeBuiltinTypes( BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, NF, MaskedPolicyScheme, P, IsTuple); std::optional PolicyTypes = TypeCache.computeTypes(BT, Log2LMUL, NF, PolicyPrototype); Out.push_back(std::make_unique( Name, SuffixStr, OverloadedName, OverloadedSuffixStr, MaskedIRName, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, ManualCodegen, *PolicyTypes, IntrinsicTypes, RequiredFeatures, NF, P, HasFRMRoundModeOp)); } } // 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; if (!SemaRecords) 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.RequiredExtensions = 0; for (auto RequiredFeature : RequiredFeatures) { RVVRequire RequireExt = StringSwitch(RequiredFeature) .Case("RV64", RVV_REQ_RV64) .Case("Zvfhmin", RVV_REQ_Zvfhmin) .Case("Xsfvcp", RVV_REQ_Xsfvcp) .Case("Xsfvfnrclipxfqf", RVV_REQ_Xsfvfnrclipxfqf) .Case("Xsfvfwmaccqqq", RVV_REQ_Xsfvfwmaccqqq) .Case("Xsfvqmaccdod", RVV_REQ_Xsfvqmaccdod) .Case("Xsfvqmaccqoq", RVV_REQ_Xsfvqmaccqoq) .Case("Zvbb", RVV_REQ_Zvbb) .Case("Zvbc", RVV_REQ_Zvbc) .Case("Zvkb", RVV_REQ_Zvkb) .Case("Zvkg", RVV_REQ_Zvkg) .Case("Zvkned", RVV_REQ_Zvkned) .Case("Zvknha", RVV_REQ_Zvknha) .Case("Zvknhb", RVV_REQ_Zvknhb) .Case("Zvksed", RVV_REQ_Zvksed) .Case("Zvksh", RVV_REQ_Zvksh) .Case("Experimental", RVV_REQ_Experimental) .Default(RVV_REQ_None); assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?"); SR.RequiredExtensions |= RequireExt; } SR.NF = NF; SR.HasMasked = HasMasked; SR.HasVL = HasVL; SR.HasMaskedOffOperand = HasMaskedOffOperand; SR.HasTailPolicy = HasTailPolicy; SR.HasMaskPolicy = HasMaskPolicy; SR.UnMaskedPolicyScheme = static_cast(UnMaskedPolicyScheme); SR.MaskedPolicyScheme = static_cast(MaskedPolicyScheme); SR.Prototype = std::move(BasicPrototype); SR.Suffix = parsePrototypes(SuffixProto); SR.OverloadedSuffix = parsePrototypes(OverloadedSuffixProto); SR.IsTuple = IsTuple; SR.HasFRMRoundModeOp = HasFRMRoundModeOp; SemaRecords->push_back(SR); } } void RVVEmitter::printHeaderCode(raw_ostream &OS) { std::vector RVVHeaders = Records.getAllDerivedDefinitions("RVVHeader"); for (auto *R : RVVHeaders) { StringRef HeaderCodeStr = R->getValueAsString("HeaderCode"); OS << HeaderCodeStr.str(); } } void RVVEmitter::createRVVIntrinsicRecords(std::vector &Out, SemaSignatureTable &SST, ArrayRef SemaRecords) { SST.init(SemaRecords); for (const auto &SR : SemaRecords) { Out.emplace_back(RVVIntrinsicRecord()); RVVIntrinsicRecord &R = Out.back(); R.Name = SR.Name.c_str(); R.OverloadedName = SR.OverloadedName.c_str(); R.PrototypeIndex = SST.getIndex(SR.Prototype); R.SuffixIndex = SST.getIndex(SR.Suffix); R.OverloadedSuffixIndex = SST.getIndex(SR.OverloadedSuffix); R.PrototypeLength = SR.Prototype.size(); R.SuffixLength = SR.Suffix.size(); R.OverloadedSuffixSize = SR.OverloadedSuffix.size(); R.RequiredExtensions = SR.RequiredExtensions; R.TypeRangeMask = SR.TypeRangeMask; R.Log2LMULMask = SR.Log2LMULMask; R.NF = SR.NF; R.HasMasked = SR.HasMasked; R.HasVL = SR.HasVL; R.HasMaskedOffOperand = SR.HasMaskedOffOperand; R.HasTailPolicy = SR.HasTailPolicy; R.HasMaskPolicy = SR.HasMaskPolicy; R.UnMaskedPolicyScheme = SR.UnMaskedPolicyScheme; R.MaskedPolicyScheme = SR.MaskedPolicyScheme; R.IsTuple = SR.IsTuple; R.HasFRMRoundModeOp = SR.HasFRMRoundModeOp; assert(R.PrototypeIndex != static_cast(SemaSignatureTable::INVALID_INDEX)); assert(R.SuffixIndex != static_cast(SemaSignatureTable::INVALID_INDEX)); assert(R.OverloadedSuffixIndex != static_cast(SemaSignatureTable::INVALID_INDEX)); } } void RVVEmitter::createSema(raw_ostream &OS) { std::vector> Defs; std::vector RVVIntrinsicRecords; SemaSignatureTable SST; std::vector SemaRecords; createRVVIntrinsics(Defs, &SemaRecords); createRVVIntrinsicRecords(RVVIntrinsicRecords, SST, SemaRecords); // Emit signature table for SemaRISCVVectorLookup.cpp. OS << "#ifdef DECL_SIGNATURE_TABLE\n"; SST.print(OS); OS << "#endif\n"; // Emit RVVIntrinsicRecords for SemaRISCVVectorLookup.cpp. OS << "#ifdef DECL_INTRINSIC_RECORDS\n"; for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords) OS << Record; OS << "#endif\n"; } namespace clang { void EmitRVVHeader(RecordKeeper &Records, raw_ostream &OS) { RVVEmitter(Records).createHeader(OS); } void EmitRVVBuiltins(RecordKeeper &Records, raw_ostream &OS) { RVVEmitter(Records).createBuiltins(OS); } void EmitRVVBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { RVVEmitter(Records).createCodeGen(OS); } void EmitRVVBuiltinSema(RecordKeeper &Records, raw_ostream &OS) { RVVEmitter(Records).createSema(OS); } } // End namespace clang