| //===- RISCVTargetDefEmitter.cpp - Generate lists of RISC-V CPUs ----------===// |
| // |
| // 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 emits the include file needed by RISCVTargetParser.cpp |
| // and RISCVISAInfo.cpp to parse the RISC-V CPUs and extensions. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include "llvm/Support/RISCVISAUtils.h" |
| #include "llvm/TableGen/Error.h" |
| #include "llvm/TableGen/Record.h" |
| #include "llvm/TableGen/StringToOffsetTable.h" |
| #include "llvm/TableGen/TableGenBackend.h" |
| |
| using namespace llvm; |
| |
| static StringRef getExtensionName(const Record *R) { |
| StringRef Name = R->getValueAsString("Name"); |
| Name.consume_front("experimental-"); |
| return Name; |
| } |
| |
| static void printExtensionTable(raw_ostream &OS, |
| ArrayRef<const Record *> Extensions, |
| bool Experimental) { |
| OS << "static const RISCVSupportedExtension Supported"; |
| if (Experimental) |
| OS << "Experimental"; |
| OS << "Extensions[] = {\n"; |
| |
| for (const Record *R : Extensions) { |
| if (R->getValueAsBit("Experimental") != Experimental) |
| continue; |
| |
| OS.indent(4) << "{\"" << getExtensionName(R) << "\", {" |
| << R->getValueAsInt("MajorVersion") << ", " |
| << R->getValueAsInt("MinorVersion") << "}},\n"; |
| } |
| |
| OS << "};\n\n"; |
| } |
| |
| static void emitRISCVExtensions(const RecordKeeper &Records, raw_ostream &OS) { |
| OS << "#ifdef GET_SUPPORTED_EXTENSIONS\n"; |
| OS << "#undef GET_SUPPORTED_EXTENSIONS\n\n"; |
| |
| std::vector<const Record *> Extensions = |
| Records.getAllDerivedDefinitionsIfDefined("RISCVExtension"); |
| llvm::sort(Extensions, [](const Record *Rec1, const Record *Rec2) { |
| return getExtensionName(Rec1) < getExtensionName(Rec2); |
| }); |
| |
| if (!Extensions.empty()) { |
| printExtensionTable(OS, Extensions, /*Experimental=*/false); |
| printExtensionTable(OS, Extensions, /*Experimental=*/true); |
| } |
| |
| OS << "#endif // GET_SUPPORTED_EXTENSIONS\n\n"; |
| |
| OS << "#ifdef GET_IMPLIED_EXTENSIONS\n"; |
| OS << "#undef GET_IMPLIED_EXTENSIONS\n\n"; |
| |
| if (!Extensions.empty()) { |
| OS << "\nstatic constexpr ImpliedExtsEntry ImpliedExts[] = {\n"; |
| for (const Record *Ext : Extensions) { |
| std::vector<const Record *> ImpliesList = |
| Ext->getValueAsListOfDefs("Implies"); |
| if (ImpliesList.empty()) |
| continue; |
| |
| StringRef Name = getExtensionName(Ext); |
| |
| for (const Record *ImpliedExt : ImpliesList) { |
| if (!ImpliedExt->isSubClassOf("RISCVExtension")) |
| continue; |
| |
| OS.indent(4) << "{ {\"" << Name << "\"}, \"" |
| << getExtensionName(ImpliedExt) << "\"},\n"; |
| } |
| } |
| |
| OS << "};\n\n"; |
| } |
| |
| OS << "#endif // GET_IMPLIED_EXTENSIONS\n\n"; |
| } |
| |
| // We can generate march string from target features as what has been described |
| // in RISC-V ISA specification (version 20191213) 'Chapter 27. ISA Extension |
| // Naming Conventions'. |
| // |
| // This is almost the same as RISCVFeatures::parseFeatureBits, except that we |
| // get feature name from feature records instead of feature bits. |
| static void printMArch(raw_ostream &OS, ArrayRef<const Record *> Features) { |
| RISCVISAUtils::OrderedExtensionMap Extensions; |
| unsigned XLen = 0; |
| |
| // Convert features to FeatureVector. |
| for (const Record *Feature : Features) { |
| StringRef FeatureName = getExtensionName(Feature); |
| if (Feature->isSubClassOf("RISCVExtension")) { |
| unsigned Major = Feature->getValueAsInt("MajorVersion"); |
| unsigned Minor = Feature->getValueAsInt("MinorVersion"); |
| Extensions[FeatureName.str()] = {Major, Minor}; |
| } else if (FeatureName == "64bit") { |
| assert(XLen == 0 && "Already determined XLen"); |
| XLen = 64; |
| } else if (FeatureName == "32bit") { |
| assert(XLen == 0 && "Already determined XLen"); |
| XLen = 32; |
| } |
| } |
| |
| assert(XLen != 0 && "Unable to determine XLen"); |
| |
| OS << "rv" << XLen; |
| |
| ListSeparator LS("_"); |
| for (auto const &Ext : Extensions) |
| OS << LS << Ext.first << Ext.second.Major << 'p' << Ext.second.Minor; |
| } |
| |
| static void printProfileTable(raw_ostream &OS, |
| ArrayRef<const Record *> Profiles, |
| bool Experimental) { |
| OS << "static constexpr RISCVProfile Supported"; |
| if (Experimental) |
| OS << "Experimental"; |
| OS << "Profiles[] = {\n"; |
| |
| for (const Record *Rec : Profiles) { |
| if (Rec->getValueAsBit("Experimental") != Experimental) |
| continue; |
| |
| StringRef Name = Rec->getValueAsString("Name"); |
| Name.consume_front("experimental-"); |
| OS.indent(4) << "{\"" << Name << "\",\""; |
| printMArch(OS, Rec->getValueAsListOfDefs("Implies")); |
| OS << "\"},\n"; |
| } |
| |
| OS << "};\n\n"; |
| } |
| |
| static void emitRISCVProfiles(const RecordKeeper &Records, raw_ostream &OS) { |
| OS << "#ifdef GET_SUPPORTED_PROFILES\n"; |
| OS << "#undef GET_SUPPORTED_PROFILES\n\n"; |
| |
| ArrayRef<const Record *> Profiles = |
| Records.getAllDerivedDefinitionsIfDefined("RISCVProfile"); |
| |
| if (!Profiles.empty()) { |
| printProfileTable(OS, Profiles, /*Experimental=*/false); |
| bool HasExperimentalProfiles = any_of(Profiles, [&](const Record *Rec) { |
| return Rec->getValueAsBit("Experimental"); |
| }); |
| if (HasExperimentalProfiles) |
| printProfileTable(OS, Profiles, /*Experimental=*/true); |
| } |
| |
| OS << "#endif // GET_SUPPORTED_PROFILES\n\n"; |
| } |
| |
| static void emitRISCVProcs(const RecordKeeper &RK, raw_ostream &OS) { |
| OS << "#ifndef PROC\n" |
| << "#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN" |
| << ", FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID)\n" |
| << "#endif\n\n"; |
| |
| // Iterate on all definition records. |
| for (const Record *Rec : |
| RK.getAllDerivedDefinitionsIfDefined("RISCVProcessorModel")) { |
| std::vector<const Record *> Features = |
| Rec->getValueAsListOfDefs("Features"); |
| bool FastScalarUnalignedAccess = |
| any_of(Features, [&](const Record *Feature) { |
| return Feature->getValueAsString("Name") == "unaligned-scalar-mem"; |
| }); |
| |
| bool FastVectorUnalignedAccess = |
| any_of(Features, [&](const Record *Feature) { |
| return Feature->getValueAsString("Name") == "unaligned-vector-mem"; |
| }); |
| |
| OS << "PROC(" << Rec->getName() << ", {\"" << Rec->getValueAsString("Name") |
| << "\"}, {\""; |
| |
| StringRef MArch = Rec->getValueAsString("DefaultMarch"); |
| |
| // Compute MArch from features if we don't specify it. |
| if (MArch.empty()) |
| printMArch(OS, Features); |
| else |
| OS << MArch; |
| |
| uint32_t MVendorID = Rec->getValueAsInt("MVendorID"); |
| uint64_t MArchID = Rec->getValueAsInt("MArchID"); |
| uint64_t MImpID = Rec->getValueAsInt("MImpID"); |
| |
| OS << "\"}, " << FastScalarUnalignedAccess << ", " |
| << FastVectorUnalignedAccess; |
| OS << ", " << format_hex(MVendorID, 10); |
| OS << ", " << format_hex(MArchID, 18); |
| OS << ", " << format_hex(MImpID, 18); |
| OS << ")\n"; |
| } |
| OS << "\n#undef PROC\n"; |
| OS << "\n"; |
| OS << "#ifndef TUNE_PROC\n" |
| << "#define TUNE_PROC(ENUM, NAME)\n" |
| << "#endif\n\n"; |
| |
| for (const Record *Rec : |
| RK.getAllDerivedDefinitionsIfDefined("RISCVTuneProcessorModel")) { |
| OS << "TUNE_PROC(" << Rec->getName() << ", " |
| << "\"" << Rec->getValueAsString("Name") << "\")\n"; |
| } |
| |
| OS << "\n#undef TUNE_PROC\n"; |
| } |
| |
| static void emitRISCVExtensionBitmask(const RecordKeeper &RK, raw_ostream &OS) { |
| std::vector<const Record *> Extensions = |
| RK.getAllDerivedDefinitionsIfDefined("RISCVExtensionBitmask"); |
| llvm::sort(Extensions, [](const Record *Rec1, const Record *Rec2) { |
| unsigned GroupID1 = Rec1->getValueAsInt("GroupID"); |
| unsigned GroupID2 = Rec2->getValueAsInt("GroupID"); |
| if (GroupID1 != GroupID2) |
| return GroupID1 < GroupID2; |
| |
| return Rec1->getValueAsInt("BitPos") < Rec2->getValueAsInt("BitPos"); |
| }); |
| |
| #ifndef NDEBUG |
| llvm::DenseSet<std::pair<uint64_t, uint64_t>> Seen; |
| #endif |
| |
| OS << "#ifdef GET_RISCVExtensionBitmaskTable_IMPL\n"; |
| OS << "static const RISCVExtensionBitmask ExtensionBitmask[]={\n"; |
| for (const Record *Rec : Extensions) { |
| unsigned GroupIDVal = Rec->getValueAsInt("GroupID"); |
| unsigned BitPosVal = Rec->getValueAsInt("BitPos"); |
| |
| StringRef ExtName = Rec->getValueAsString("Name"); |
| ExtName.consume_front("experimental-"); |
| |
| #ifndef NDEBUG |
| assert(Seen.insert({GroupIDVal, BitPosVal}).second && "duplicated bitmask"); |
| #endif |
| |
| OS.indent(4) << "{" |
| << "\"" << ExtName << "\"" |
| << ", " << GroupIDVal << ", " << BitPosVal << "ULL" |
| << "},\n"; |
| } |
| OS << "};\n"; |
| OS << "#endif\n\n"; |
| } |
| |
| static void emitRISCVTuneFeatures(const RecordKeeper &RK, |
| StringToOffsetTable &StrTable, |
| raw_ostream &OS) { |
| std::vector<const Record *> TuneFeatureRecords = |
| RK.getAllDerivedDefinitionsIfDefined("RISCVTuneFeature"); |
| |
| // {Post Directive Idx, Neg Directive Idx, TuneFeature Record} |
| SmallVector<std::tuple<unsigned, unsigned, const Record *>> |
| TuneFeatureDirectives; |
| // {Directive Idx -> Original Record} |
| // This is primarily for diagnosing purposes -- when there is a duplication, |
| // we are able to pointed out the previous definition. |
| DenseMap<unsigned, const Record *> DirectiveToRecord; |
| // A list of {Feature Name, Implied Feature Name} |
| SmallVector<std::pair<StringRef, StringRef>> ImpliedFeatureList; |
| |
| for (const auto *R : TuneFeatureRecords) { |
| // Preemptively insert feature name into the string table because we know |
| // it will be used later. |
| StringRef FeatureName = R->getValueAsString("Name"); |
| StrTable.GetOrAddStringOffset(FeatureName); |
| |
| StringRef PosName = R->getValueAsString("PositiveDirectiveName"); |
| StringRef NegName = R->getValueAsString("NegativeDirectiveName"); |
| unsigned PosIdx = StrTable.GetOrAddStringOffset(PosName); |
| if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(PosIdx, R); |
| !Inserted) { |
| PrintError(R, "RISC-V tune feature positive directive '" + |
| Twine(PosName) + "' was already defined"); |
| PrintFatalNote(ItEntry->second, "Previously defined here"); |
| } |
| unsigned NegIdx = StrTable.GetOrAddStringOffset(NegName); |
| if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(NegIdx, R); |
| !Inserted) { |
| PrintError(R, "RISC-V tune feature negative directive '" + |
| Twine(NegName) + "' was already defined"); |
| PrintFatalNote(ItEntry->second, "Previously defined here"); |
| } |
| |
| TuneFeatureDirectives.emplace_back(PosIdx, NegIdx, R); |
| } |
| |
| for (const auto *R : TuneFeatureRecords) { |
| std::vector<const Record *> Implies = R->getValueAsListOfDefs("Implies"); |
| for (const auto *ImpliedRecord : Implies) { |
| StringRef CurrFeatureName = R->getValueAsString("Name"); |
| StringRef ImpliedFeatureName = ImpliedRecord->getValueAsString("Name"); |
| |
| ImpliedFeatureList.emplace_back(CurrFeatureName, ImpliedFeatureName); |
| } |
| } |
| |
| OS << "#ifdef GET_TUNE_FEATURES\n"; |
| OS << "#undef GET_TUNE_FEATURES\n\n"; |
| |
| StrTable.EmitStringTableDef(OS, "TuneFeatureStrings"); |
| OS << "\n"; |
| |
| OS << "static constexpr RISCVTuneFeature TuneFeatures[] = {\n"; |
| for (const auto &[PosIdx, NegIdx, R] : TuneFeatureDirectives) { |
| StringRef FeatureName = R->getValueAsString("Name"); |
| OS.indent(4) << formatv("{{ {0}, {1}, {2} },\t// '{3}'\n", PosIdx, NegIdx, |
| *StrTable.GetStringOffset(FeatureName), |
| FeatureName); |
| } |
| OS << "};\n\n"; |
| |
| OS << "static constexpr RISCVImpliedTuneFeature ImpliedTuneFeatures[] = {\n"; |
| for (auto [Feature, ImpliedFeature] : ImpliedFeatureList) |
| OS.indent(4) << formatv("{{ {0}, {1} }, // '{2}' -> '{3}'\n", |
| *StrTable.GetStringOffset(Feature), |
| *StrTable.GetStringOffset(ImpliedFeature), Feature, |
| ImpliedFeature); |
| OS << "};\n\n"; |
| |
| OS << "#endif // GET_TUNE_FEATURES\n\n"; |
| } |
| |
| static void |
| emitRISCVConfigurableTuneFeatures(const RecordKeeper &RK, |
| const StringToOffsetTable &StrTable, |
| raw_ostream &OS) { |
| std::vector<const Record *> AllProcModels = |
| RK.getAllDerivedDefinitionsIfDefined("ProcessorModel"); |
| |
| OS << "#ifdef GET_CONFIGURABLE_TUNE_FEATURES\n"; |
| OS << "#undef GET_CONFIGURABLE_TUNE_FEATURES\n\n"; |
| |
| OS << "static constexpr RISCVConfigurableTuneFeatures " |
| "ConfigurableTuneFeatures[] = {\n"; |
| |
| for (const Record *Proc : AllProcModels) { |
| StringRef ProcName = Proc->getValueAsString("Name"); |
| std::vector<const Record *> TuneFeatures = |
| Proc->getValueAsListOfDefs("ConfigurableTuneFeatures"); |
| for (const Record *TF : TuneFeatures) { |
| unsigned PosDirectiveIdx = *StrTable.GetStringOffset( |
| TF->getValueAsString("PositiveDirectiveName")); |
| unsigned NegDirectiveIdx = *StrTable.GetStringOffset( |
| TF->getValueAsString("NegativeDirectiveName")); |
| OS.indent(4) << formatv("{{ {{ \"{0}\" }, {1} },\n", ProcName, |
| PosDirectiveIdx); |
| OS.indent(4) << formatv("{{ {{ \"{0}\" }, {1} },\n", ProcName, |
| NegDirectiveIdx); |
| } |
| } |
| |
| OS << "};\n\n"; |
| OS << "#endif // GET_CONFIGURABLE_TUNE_FEATURES\n"; |
| } |
| |
| static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) { |
| emitRISCVExtensions(RK, OS); |
| emitRISCVProfiles(RK, OS); |
| emitRISCVProcs(RK, OS); |
| emitRISCVExtensionBitmask(RK, OS); |
| |
| StringToOffsetTable TuneFeatureStrTable; |
| emitRISCVTuneFeatures(RK, TuneFeatureStrTable, OS); |
| emitRISCVConfigurableTuneFeatures(RK, TuneFeatureStrTable, OS); |
| } |
| |
| static TableGen::Emitter::Opt X("gen-riscv-target-def", emitRiscvTargetDef, |
| "Generate the list of CPUs and extensions for " |
| "RISC-V"); |