| //===- ExegesisEmitter.cpp - Generate exegesis target data ----------------===// | |
| // | |
| // 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 llvm-exegesis information. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SmallSet.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/MathExtras.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <map> | |
| #include <string> | |
| #include <vector> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "exegesis-emitter" | |
| namespace { | |
| class ExegesisEmitter { | |
| public: | |
| ExegesisEmitter(const RecordKeeper &RK); | |
| void run(raw_ostream &OS) const; | |
| private: | |
| unsigned getPfmCounterId(llvm::StringRef Name) const { | |
| const auto It = PfmCounterNameTable.find(Name); | |
| if (It == PfmCounterNameTable.end()) | |
| PrintFatalError("no pfm counter id for " + Name); | |
| return It->second; | |
| } | |
| // Collects all the ProcPfmCounters definitions available in this target. | |
| void emitPfmCounters(raw_ostream &OS) const; | |
| void emitPfmCountersInfo(const Record &Def, | |
| unsigned &IssueCountersTableOffset, | |
| raw_ostream &OS) const; | |
| void emitPfmCountersLookupTable(raw_ostream &OS) const; | |
| const RecordKeeper &Records; | |
| std::string Target; | |
| // Table of counter name -> counter index. | |
| const std::map<llvm::StringRef, unsigned> PfmCounterNameTable; | |
| }; | |
| struct ValidationCounterInfo { | |
| int64_t EventNumber; | |
| StringRef EventName; | |
| unsigned PfmCounterID; | |
| }; | |
| } // namespace | |
| static std::map<llvm::StringRef, unsigned> | |
| collectPfmCounters(const RecordKeeper &Records) { | |
| std::map<llvm::StringRef, unsigned> PfmCounterNameTable; | |
| const auto AddPfmCounterName = [&PfmCounterNameTable]( | |
| const Record *PfmCounterDef) { | |
| const llvm::StringRef Counter = PfmCounterDef->getValueAsString("Counter"); | |
| if (!Counter.empty()) | |
| PfmCounterNameTable.emplace(Counter, 0); | |
| }; | |
| for (const Record *Def : | |
| Records.getAllDerivedDefinitions("ProcPfmCounters")) { | |
| // Check that ResourceNames are unique. | |
| llvm::SmallSet<llvm::StringRef, 16> Seen; | |
| for (const Record *IssueCounter : | |
| Def->getValueAsListOfDefs("IssueCounters")) { | |
| const llvm::StringRef ResourceName = | |
| IssueCounter->getValueAsString("ResourceName"); | |
| if (ResourceName.empty()) | |
| PrintFatalError(IssueCounter->getLoc(), "invalid empty ResourceName"); | |
| if (!Seen.insert(ResourceName).second) | |
| PrintFatalError(IssueCounter->getLoc(), | |
| "duplicate ResourceName " + ResourceName); | |
| AddPfmCounterName(IssueCounter); | |
| } | |
| for (const Record *ValidationCounter : | |
| Def->getValueAsListOfDefs("ValidationCounters")) | |
| AddPfmCounterName(ValidationCounter); | |
| AddPfmCounterName(Def->getValueAsDef("CycleCounter")); | |
| AddPfmCounterName(Def->getValueAsDef("UopsCounter")); | |
| } | |
| unsigned Index = 0; | |
| for (auto &NameAndIndex : PfmCounterNameTable) | |
| NameAndIndex.second = Index++; | |
| return PfmCounterNameTable; | |
| } | |
| ExegesisEmitter::ExegesisEmitter(const RecordKeeper &RK) | |
| : Records(RK), PfmCounterNameTable(collectPfmCounters(RK)) { | |
| ArrayRef<const Record *> Targets = Records.getAllDerivedDefinitions("Target"); | |
| if (Targets.size() == 0) | |
| PrintFatalError("No 'Target' subclasses defined!"); | |
| if (Targets.size() != 1) | |
| PrintFatalError("Multiple subclasses of Target defined!"); | |
| Target = Targets[0]->getName().str(); | |
| } | |
| static bool EventNumberLess(const ValidationCounterInfo &LHS, | |
| const ValidationCounterInfo &RHS) { | |
| return LHS.EventNumber < RHS.EventNumber; | |
| } | |
| void ExegesisEmitter::emitPfmCountersInfo(const Record &Def, | |
| unsigned &IssueCountersTableOffset, | |
| raw_ostream &OS) const { | |
| const Record *CycleCounterDef = Def.getValueAsDef("CycleCounter"); | |
| const auto CycleCounter = CycleCounterDef->getValueAsString("Counter"); | |
| const Record *UopsCounterDef = Def.getValueAsDef("UopsCounter"); | |
| const auto UopsCounter = UopsCounterDef->getValueAsString("Counter"); | |
| const size_t NumIssueCounters = | |
| Def.getValueAsListOfDefs("IssueCounters").size(); | |
| const size_t NumValidationCounters = | |
| Def.getValueAsListOfDefs("ValidationCounters").size(); | |
| // Emit Validation Counters Array | |
| if (NumValidationCounters != 0) { | |
| std::vector<ValidationCounterInfo> ValidationCounters; | |
| ValidationCounters.reserve(NumValidationCounters); | |
| for (const Record *ValidationCounter : | |
| Def.getValueAsListOfDefs("ValidationCounters")) { | |
| ValidationCounters.push_back( | |
| {ValidationCounter->getValueAsDef("EventType") | |
| ->getValueAsInt("EventNumber"), | |
| ValidationCounter->getValueAsDef("EventType")->getName(), | |
| getPfmCounterId(ValidationCounter->getValueAsString("Counter"))}); | |
| } | |
| std::sort(ValidationCounters.begin(), ValidationCounters.end(), | |
| EventNumberLess); | |
| OS << "\nstatic const std::pair<ValidationEvent, const char*> " << Target | |
| << Def.getName() << "ValidationCounters[] = {\n"; | |
| for (const ValidationCounterInfo &VCI : ValidationCounters) { | |
| OS << " { " << VCI.EventName << ", " << Target << "PfmCounterNames[" | |
| << VCI.PfmCounterID << "]},\n"; | |
| } | |
| OS << "};\n"; | |
| } | |
| OS << "\nstatic const PfmCountersInfo " << Target << Def.getName() | |
| << " = {\n"; | |
| // Cycle Counter. | |
| if (CycleCounterDef->getValueAsInt("EventSelect") != -1) { | |
| const int64_t EventSelect = CycleCounterDef->getValueAsInt("EventSelect"); | |
| if (!isInt<16>(EventSelect)) | |
| PrintFatalError(CycleCounterDef->getLoc(), | |
| "EventSelect must fit in 16 bits"); | |
| const int64_t UMask = CycleCounterDef->getValueAsInt("UMask"); | |
| if (!isInt<8>(UMask)) | |
| PrintFatalError(CycleCounterDef->getLoc(), "UMask must fit in 8 bits"); | |
| OS << " nullptr, // Cycle counter uses raw encoding below.\n"; | |
| OS << " 0x" << format_hex_no_prefix(EventSelect, 4) << ", 0x" | |
| << format_hex_no_prefix(UMask, 2) << ", // Raw cycle counter\n"; | |
| } else if (CycleCounter.empty()) { | |
| OS << " nullptr, // No cycle counter.\n"; | |
| OS << " -1, 0, // No raw cycle counter\n"; | |
| } else { | |
| OS << " " << Target << "PfmCounterNames[" << getPfmCounterId(CycleCounter) | |
| << "], // Cycle counter\n"; | |
| OS << " -1, 0, // No raw cycle counter\n"; | |
| } | |
| // Uops Counter. | |
| if (UopsCounterDef->getValueAsInt("EventSelect") != -1) { | |
| const int64_t EventSelect = UopsCounterDef->getValueAsInt("EventSelect"); | |
| if (!isInt<16>(EventSelect)) | |
| PrintFatalError(UopsCounterDef->getLoc(), | |
| "EventSelect must fit in 16 bits"); | |
| const int64_t UMask = UopsCounterDef->getValueAsInt("UMask"); | |
| if (!isInt<8>(UMask)) | |
| PrintFatalError(UopsCounterDef->getLoc(), "UMask must fit in 8 bits"); | |
| OS << " nullptr, // Uops counter uses raw encoding below.\n"; | |
| OS << " 0x" << format_hex_no_prefix(EventSelect, 4) << ", 0x" | |
| << format_hex_no_prefix(UMask, 2) << ", // Raw uops counter\n"; | |
| } else if (UopsCounter.empty()) { | |
| OS << " nullptr, // No uops counter.\n"; | |
| OS << " -1, 0, // No raw uops counter\n"; | |
| } else { | |
| OS << " " << Target << "PfmCounterNames[" << getPfmCounterId(UopsCounter) | |
| << "], // Uops counter\n"; | |
| OS << " -1, 0, // No raw uops counter\n"; | |
| } | |
| // Issue Counters | |
| if (NumIssueCounters == 0) | |
| OS << " nullptr, 0, // No issue counters\n"; | |
| else | |
| OS << " " << Target << "PfmIssueCounters + " << IssueCountersTableOffset | |
| << ", " << NumIssueCounters << ", // Issue counters.\n"; | |
| // Validation Counters | |
| if (NumValidationCounters == 0) | |
| OS << " nullptr, 0 // No validation counters.\n"; | |
| else | |
| OS << " " << Target << Def.getName() << "ValidationCounters, " | |
| << NumValidationCounters << " // Validation counters.\n"; | |
| OS << "};\n"; | |
| IssueCountersTableOffset += NumIssueCounters; | |
| } | |
| void ExegesisEmitter::emitPfmCounters(raw_ostream &OS) const { | |
| // Emit the counter name table. | |
| OS << "\nstatic const char *" << Target << "PfmCounterNames[] = {\n"; | |
| for (const auto &NameAndIndex : PfmCounterNameTable) | |
| OS << " \"" << NameAndIndex.first << "\", // " << NameAndIndex.second | |
| << "\n"; | |
| OS << "};\n\n"; | |
| // Emit the IssueCounters table. | |
| const auto PfmCounterDefs = | |
| Records.getAllDerivedDefinitions("ProcPfmCounters"); | |
| // Only emit if non-empty. | |
| const bool HasAtLeastOnePfmIssueCounter = | |
| llvm::any_of(PfmCounterDefs, [](const Record *Def) { | |
| return !Def->getValueAsListOfDefs("IssueCounters").empty(); | |
| }); | |
| if (HasAtLeastOnePfmIssueCounter) { | |
| OS << "static const PfmCountersInfo::IssueCounter " << Target | |
| << "PfmIssueCounters[] = {\n"; | |
| for (const Record *Def : PfmCounterDefs) { | |
| for (const Record *ICDef : Def->getValueAsListOfDefs("IssueCounters")) | |
| OS << " { " << Target << "PfmCounterNames[" | |
| << getPfmCounterId(ICDef->getValueAsString("Counter")) << "], \"" | |
| << ICDef->getValueAsString("ResourceName") << "\"},\n"; | |
| } | |
| OS << "};\n"; | |
| } | |
| // Now generate the PfmCountersInfo. | |
| unsigned IssueCountersTableOffset = 0; | |
| for (const Record *Def : PfmCounterDefs) | |
| emitPfmCountersInfo(*Def, IssueCountersTableOffset, OS); | |
| OS << "\n"; | |
| } | |
| void ExegesisEmitter::emitPfmCountersLookupTable(raw_ostream &OS) const { | |
| std::vector<const Record *> Bindings = | |
| Records.getAllDerivedDefinitions("PfmCountersBinding"); | |
| assert(!Bindings.empty() && "there must be at least one binding"); | |
| llvm::sort(Bindings, [](const Record *L, const Record *R) { | |
| return L->getValueAsString("CpuName") < R->getValueAsString("CpuName"); | |
| }); | |
| OS << "// Sorted (by CpuName) array of pfm counters.\n" | |
| << "static const CpuAndPfmCounters " << Target << "CpuPfmCounters[] = {\n"; | |
| for (const Record *Binding : Bindings) { | |
| // Emit as { "cpu", procinit }, | |
| OS << " { \"" // | |
| << Binding->getValueAsString("CpuName") << "\"," // | |
| << " &" << Target << Binding->getValueAsDef("Counters")->getName() // | |
| << " },\n"; | |
| } | |
| OS << "};\n\n"; | |
| } | |
| void ExegesisEmitter::run(raw_ostream &OS) const { | |
| emitSourceFileHeader("Exegesis Tables", OS); | |
| emitPfmCounters(OS); | |
| emitPfmCountersLookupTable(OS); | |
| } | |
| static TableGen::Emitter::OptClass<ExegesisEmitter> | |
| X("gen-exegesis", "Generate llvm-exegesis tables"); |