| //===- 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 <numeric> |
| |
| 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. |
| unsigned RequiredExtensions; |
| |
| // Prototype for this intrinsic. |
| SmallVector<PrototypeDescriptor> Prototype; |
| |
| // Suffix of intrinsic name. |
| SmallVector<PrototypeDescriptor> Suffix; |
| |
| // Suffix of overloaded intrinsic name. |
| SmallVector<PrototypeDescriptor> OverloadedSuffix; |
| |
| // BitMask for supported policies. |
| uint16_t PolicyBitMask; |
| |
| // Number of field, large than 1 if it's segment load/store. |
| unsigned NF; |
| |
| bool HasMasked :1; |
| bool HasVL :1; |
| bool HasMaskedOffOperand :1; |
| bool IsPrototypeDefaultTU : 1; |
| bool HasTailPolicy : 1; |
| bool HasMaskPolicy : 1; |
| uint8_t UnMaskedPolicyScheme : 2; |
| uint8_t MaskedPolicyScheme : 2; |
| }; |
| |
| // Compressed function signature table. |
| class SemaSignatureTable { |
| private: |
| std::vector<PrototypeDescriptor> SignatureTable; |
| |
| void insert(ArrayRef<PrototypeDescriptor> Signature); |
| |
| public: |
| static constexpr unsigned INVALID_INDEX = ~0U; |
| |
| // Create compressed signature table from SemaRecords. |
| void init(ArrayRef<SemaRecord> SemaRecords); |
| |
| // Query the Signature, return INVALID_INDEX if not found. |
| unsigned getIndex(ArrayRef<PrototypeDescriptor> Signature); |
| |
| /// Print signature table in RVVHeader Record to \p OS |
| void print(raw_ostream &OS); |
| }; |
| |
| class RVVEmitter { |
| private: |
| RecordKeeper &Records; |
| |
| 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<std::unique_ptr<RVVIntrinsic>> &Out, |
| std::vector<SemaRecord> *SemaRecords = nullptr); |
| /// Create all intrinsic records and SemaSignatureTable from SemaRecords. |
| void createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out, |
| SemaSignatureTable &SST, |
| ArrayRef<SemaRecord> 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; |
| |
| default: |
| return BasicType::Unknown; |
| } |
| } |
| |
| 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"; |
| // We had initialized DefaultPolicy as TU/TUMU in CodeGen function. |
| if (RVVI->getDefaultPolicy() != Policy::TU && |
| RVVI->getDefaultPolicy() != Policy::TUMU && !RVVI->hasPassthruOperand() && |
| !RVVI->hasManualCodegen() && RVVI->hasVL()) |
| OS << " DefaultPolicy = " << RVVI->getDefaultPolicyBits() << ";\n"; |
| |
| if (RVVI->hasManualCodegen()) { |
| OS << " DefaultPolicy = " << RVVI->getDefaultPolicyBits() << ";\n"; |
| OS << RVVI->getManualCodegen(); |
| OS << "break;\n"; |
| return; |
| } |
| |
| // Cast pointer operand of vector load intrinsic. |
| for (const auto &I : enumerate(RVVI->getInputTypes())) { |
| if (I.value()->isPointer()) { |
| assert(RVVI->getIntrinsicTypes().front() == -1 && |
| "RVVI should be vector load intrinsic."); |
| OS << " Ops[" << I.index() << "] = Builder.CreateBitCast(Ops["; |
| OS << I.index() << "], ResultType->getPointerTo());\n"; |
| } |
| } |
| |
| 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()," |
| " DefaultPolicy));\n"; |
| if (RVVI->hasMaskedOffOperand() && |
| RVVI->getDefaultPolicy() == Policy::TAMA) |
| OS << " Ops.insert(Ops.begin(), llvm::UndefValue::get(ResultType));\n"; |
| // Masked reduction cases. |
| if (!RVVI->hasMaskedOffOperand() && RVVI->hasPassthruOperand() && |
| RVVI->getDefaultPolicy() == Policy::TAMA) |
| OS << " Ops.insert(Ops.begin(), llvm::UndefValue::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(), " |
| "DefaultPolicy));\n"; |
| else if (RVVI->hasPassthruOperand() && |
| RVVI->getDefaultPolicy() == Policy::TA) |
| OS << " Ops.insert(Ops.begin(), llvm::UndefValue::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<SemaRecord> 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<PrototypeDescriptor> &A, |
| const SmallVector<PrototypeDescriptor> &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<SmallVector<PrototypeDescriptor>, Compare> Signatures; |
| auto InsertToSignatureSet = |
| [&](const SmallVector<PrototypeDescriptor> &Signature) { |
| if (Signature.empty()) |
| return; |
| |
| Signatures.insert(Signature); |
| }; |
| |
| assert(!SemaRecords.empty()); |
| |
| llvm::for_each(SemaRecords, [&](const SemaRecord &SR) { |
| InsertToSignatureSet(SR.Prototype); |
| InsertToSignatureSet(SR.Suffix); |
| InsertToSignatureSet(SR.OverloadedSuffix); |
| }); |
| |
| llvm::for_each(Signatures, [this](auto &Sig) { insert(Sig); }); |
| } |
| |
| void SemaSignatureTable::insert(ArrayRef<PrototypeDescriptor> 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<PrototypeDescriptor> 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<int>(Sig.PT) << ", " |
| << static_cast<int>(Sig.VTM) << ", " << static_cast<int>(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 <stdint.h>\n"; |
| OS << "#include <stddef.h>\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 = RVVType::computeType(BasicType::Int8, Log2LMUL, |
| PrototypeDescriptor::Mask); |
| if (T) |
| printType(T.value()); |
| } |
| // Print RVV int/float types. |
| for (char I : StringRef("csil")) { |
| BasicType BT = ParseBasicType(I); |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BT, Log2LMUL, PrototypeDescriptor::Vector); |
| if (T) { |
| printType(T.value()); |
| auto UT = RVVType::computeType( |
| BT, Log2LMUL, |
| PrototypeDescriptor(BaseTypeModifier::Vector, |
| VectorTypeModifier::NoModifier, |
| TypeModifier::UnsignedInteger)); |
| printType(UT.value()); |
| } |
| } |
| } |
| OS << "#if defined(__riscv_zvfh)\n"; |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BasicType::Float16, Log2LMUL, |
| PrototypeDescriptor::Vector); |
| if (T) |
| printType(T.value()); |
| } |
| OS << "#endif\n"; |
| |
| OS << "#if (__riscv_v_elen_fp >= 32)\n"; |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BasicType::Float32, Log2LMUL, |
| PrototypeDescriptor::Vector); |
| if (T) |
| printType(T.value()); |
| } |
| OS << "#endif\n"; |
| |
| OS << "#if (__riscv_v_elen_fp >= 64)\n"; |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BasicType::Float64, Log2LMUL, |
| PrototypeDescriptor::Vector); |
| if (T) |
| printType(T.value()); |
| } |
| OS << "#endif\n\n"; |
| |
| 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<std::unique_ptr<RVVIntrinsic>> Defs; |
| createRVVIntrinsics(Defs); |
| |
| // Map to keep track of which builtin names have already been emitted. |
| StringMap<RVVIntrinsic *> 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<std::unique_ptr<RVVIntrinsic>> Defs; |
| createRVVIntrinsics(Defs); |
| // IR name could be empty, use the stable sort preserves the relative order. |
| llvm::stable_sort(Defs, [](const std::unique_ptr<RVVIntrinsic> &A, |
| const std::unique_ptr<RVVIntrinsic> &B) { |
| if (A->getIRName() == B->getIRName()) |
| return (A->getDefaultPolicy() < B->getDefaultPolicy()); |
| return (A->getIRName() < B->getIRName()); |
| }); |
| |
| // Map to keep track of which builtin names have already been emitted. |
| StringMap<RVVIntrinsic *> 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->getDefaultPolicy() != PrevDef->getDefaultPolicy())) { |
| 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->getNF() != Def->getNF()) |
| PrintFatalError("Builtin with same name has different NF"); |
| 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<std::unique_ptr<RVVIntrinsic>> &Out, |
| std::vector<SemaRecord> *SemaRecords) { |
| std::vector<Record *> 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<PolicyScheme>(MPSRecord->getValueAsInt("Value")); |
| Record *UMPSRecord = R->getValueAsDef("UnMaskedPolicyScheme"); |
| auto UnMaskedPolicyScheme = |
| static_cast<PolicyScheme>(UMPSRecord->getValueAsInt("Value")); |
| std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts("Log2LMUL"); |
| bool HasTailPolicy = R->getValueAsBit("HasTailPolicy"); |
| bool HasMaskPolicy = R->getValueAsBit("HasMaskPolicy"); |
| bool IsPrototypeDefaultTU = R->getValueAsBit("IsPrototypeDefaultTU"); |
| bool SupportOverloading = R->getValueAsBit("SupportOverloading"); |
| bool HasBuiltinAlias = R->getValueAsBit("HasBuiltinAlias"); |
| StringRef ManualCodegen = R->getValueAsString("ManualCodegen"); |
| StringRef MaskedManualCodegen = R->getValueAsString("MaskedManualCodegen"); |
| std::vector<int64_t> IntrinsicTypes = |
| R->getValueAsListOfInts("IntrinsicTypes"); |
| std::vector<StringRef> RequiredFeatures = |
| R->getValueAsListOfStrings("RequiredFeatures"); |
| StringRef IRName = R->getValueAsString("IRName"); |
| StringRef MaskedIRName = R->getValueAsString("MaskedIRName"); |
| unsigned NF = R->getValueAsInt("NF"); |
| |
| // If unmasked builtin supports policy, they should be TU or TA. |
| SmallVector<Policy> SupportedUnMaskedPolicies = {Policy::TU, Policy::TA}; |
| SmallVector<Policy> 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<PrototypeDescriptor> BasicPrototype = |
| parsePrototypes(Prototypes); |
| |
| SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(SuffixProto); |
| SmallVector<PrototypeDescriptor> OverloadedSuffixDesc = |
| parsePrototypes(OverloadedSuffixProto); |
| |
| // Compute Builtin types |
| auto Prototype = RVVIntrinsic::computeBuiltinTypes( |
| BasicPrototype, /*IsMasked=*/false, |
| /*HasMaskedOffOperand=*/false, HasVL, NF, IsPrototypeDefaultTU, |
| UnMaskedPolicyScheme); |
| auto MaskedPrototype = RVVIntrinsic::computeBuiltinTypes( |
| BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, NF, |
| IsPrototypeDefaultTU, MaskedPolicyScheme); |
| |
| // Create Intrinsics for each type and LMUL. |
| for (char I : TypeRange) { |
| for (int Log2LMUL : Log2LMULList) { |
| BasicType BT = ParseBasicType(I); |
| Optional<RVVTypes> Types = |
| RVVType::computeTypes(BT, Log2LMUL, NF, Prototype); |
| // Ignored to create new intrinsic if there are any illegal types. |
| if (!Types) |
| continue; |
| |
| auto SuffixStr = RVVIntrinsic::getSuffixStr(BT, Log2LMUL, SuffixDesc); |
| auto OverloadedSuffixStr = |
| RVVIntrinsic::getSuffixStr(BT, Log2LMUL, OverloadedSuffixDesc); |
| // Create a unmasked intrinsic |
| Out.push_back(std::make_unique<RVVIntrinsic>( |
| Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, |
| /*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL, |
| UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, |
| ManualCodegen, *Types, IntrinsicTypes, RequiredFeatures, NF, |
| Policy::PolicyNone, IsPrototypeDefaultTU)); |
| if (UnMaskedPolicyScheme != PolicyScheme::SchemeNone) |
| for (auto P : SupportedUnMaskedPolicies) { |
| SmallVector<PrototypeDescriptor> PolicyPrototype = |
| RVVIntrinsic::computeBuiltinTypes( |
| BasicPrototype, /*IsMasked=*/false, |
| /*HasMaskedOffOperand=*/false, HasVL, NF, |
| IsPrototypeDefaultTU, UnMaskedPolicyScheme, P); |
| Optional<RVVTypes> PolicyTypes = |
| RVVType::computeTypes(BT, Log2LMUL, NF, PolicyPrototype); |
| Out.push_back(std::make_unique<RVVIntrinsic>( |
| Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, |
| /*IsMask=*/false, /*HasMaskedOffOperand=*/false, HasVL, |
| UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, |
| ManualCodegen, PolicyTypes.value(), IntrinsicTypes, |
| RequiredFeatures, NF, P, IsPrototypeDefaultTU)); |
| } |
| if (!HasMasked) |
| continue; |
| // Create a masked intrinsic |
| Optional<RVVTypes> MaskTypes = |
| RVVType::computeTypes(BT, Log2LMUL, NF, Prototype); |
| Out.push_back(std::make_unique<RVVIntrinsic>( |
| Name, SuffixStr, OverloadedName, OverloadedSuffixStr, MaskedIRName, |
| /*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicyScheme, |
| SupportOverloading, HasBuiltinAlias, MaskedManualCodegen, |
| MaskTypes.value(), IntrinsicTypes, RequiredFeatures, NF, |
| Policy::PolicyNone, IsPrototypeDefaultTU)); |
| if (MaskedPolicyScheme == PolicyScheme::SchemeNone) |
| continue; |
| for (auto P : SupportedMaskedPolicies) { |
| SmallVector<PrototypeDescriptor> PolicyPrototype = |
| RVVIntrinsic::computeBuiltinTypes( |
| BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, |
| NF, IsPrototypeDefaultTU, MaskedPolicyScheme, P); |
| Optional<RVVTypes> PolicyTypes = |
| RVVType::computeTypes(BT, Log2LMUL, NF, PolicyPrototype); |
| Out.push_back(std::make_unique<RVVIntrinsic>( |
| Name, SuffixStr, OverloadedName, OverloadedSuffixStr, |
| MaskedIRName, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, |
| MaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, |
| MaskedManualCodegen, PolicyTypes.value(), IntrinsicTypes, |
| RequiredFeatures, NF, P, IsPrototypeDefaultTU)); |
| } |
| } // 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<unsigned>(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<RVVRequire>(RequiredFeature) |
| .Case("RV64", RVV_REQ_RV64) |
| .Case("FullMultiply", RVV_REQ_FullMultiply) |
| .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.IsPrototypeDefaultTU = IsPrototypeDefaultTU; |
| SR.HasTailPolicy = HasTailPolicy; |
| SR.HasMaskPolicy = HasMaskPolicy; |
| SR.UnMaskedPolicyScheme = static_cast<uint8_t>(UnMaskedPolicyScheme); |
| SR.MaskedPolicyScheme = static_cast<uint8_t>(MaskedPolicyScheme); |
| SR.Prototype = std::move(BasicPrototype); |
| SR.Suffix = parsePrototypes(SuffixProto); |
| SR.OverloadedSuffix = parsePrototypes(OverloadedSuffixProto); |
| |
| SemaRecords->push_back(SR); |
| } |
| } |
| |
| void RVVEmitter::printHeaderCode(raw_ostream &OS) { |
| std::vector<Record *> RVVHeaders = |
| Records.getAllDerivedDefinitions("RVVHeader"); |
| for (auto *R : RVVHeaders) { |
| StringRef HeaderCodeStr = R->getValueAsString("HeaderCode"); |
| OS << HeaderCodeStr.str(); |
| } |
| } |
| |
| void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out, |
| SemaSignatureTable &SST, |
| ArrayRef<SemaRecord> 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.IsPrototypeDefaultTU = SR.IsPrototypeDefaultTU; |
| R.HasTailPolicy = SR.HasTailPolicy; |
| R.HasMaskPolicy = SR.HasMaskPolicy; |
| R.UnMaskedPolicyScheme = SR.UnMaskedPolicyScheme; |
| R.MaskedPolicyScheme = SR.MaskedPolicyScheme; |
| |
| assert(R.PrototypeIndex != |
| static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); |
| assert(R.SuffixIndex != |
| static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); |
| assert(R.OverloadedSuffixIndex != |
| static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); |
| } |
| } |
| |
| void RVVEmitter::createSema(raw_ostream &OS) { |
| std::vector<std::unique_ptr<RVVIntrinsic>> Defs; |
| std::vector<RVVIntrinsicRecord> RVVIntrinsicRecords; |
| SemaSignatureTable SST; |
| std::vector<SemaRecord> 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 |