Author: Matt Arsenault Date: 2026-10-15T10:32:39+02:00 New Revision: 80017ba18d8c8866713586fa19245f0878b4f8a2
URL: https://github.com/llvm/llvm-project/commit/80017ba18d8c8866713586fa19245f0878b4f8a2 DIFF: https://github.com/llvm/llvm-project/commit/80017ba18d8c8866713586fa19245f0878b4f8a2.diff LOG: TableGen: Store classes and defs in a StringMap (#231573) Classes and defs were kept in a std::map ordered by name, which makes every lookup a series of string comparisons over names that often share long prefixes. Store them in a StringMap instead, and have getClasses() and getDefs() return a list sorted by name, built when first needed after records are added. Gigainstructions executed by llvm-tblgen --null-backend ``` before after AMDGPU.td 18.17 16.98 -6.6% X86.td 6.15 5.84 -5.1% AArch64.td 3.32 3.00 -9.7% RISCV.td 8.26 7.44 -9.9% ``` Including the backends, all tablegen runs in a build of all targets and clang execute 3.1% fewer instructions in total, and none are slower. Peak memory parsing AMDGPU.td drops from 835 MB to 830 MB. Co-authored-by: Claude Opus 5.5 <[email protected]> Added: Modified: clang/utils/TableGen/ClangDataCollectorsEmitter.cpp llvm/docs/TableGen/BackGuide.md llvm/include/llvm/TableGen/Record.h llvm/lib/TableGen/DetailedRecordsBackend.cpp llvm/lib/TableGen/JSONBackend.cpp llvm/lib/TableGen/Record.cpp llvm/utils/TableGen/CTagsEmitter.cpp llvm/utils/TableGen/SearchableTableEmitter.cpp mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp mlir/tools/mlir-tblgen/OpGenHelpers.cpp Removed: ################################################################################ diff --git a/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp b/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp index 50e90691c555bf..226857673d49b6 100644 --- a/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp +++ b/clang/utils/TableGen/ClangDataCollectorsEmitter.cpp @@ -17,9 +17,8 @@ using namespace llvm; void clang::EmitClangDataCollectors(const RecordKeeper &RK, raw_ostream &OS) { - const auto &Defs = RK.getClasses(); - for (const auto &Entry : Defs) { - Record &R = *Entry.second; + for (const Record *Class : RK.getClasses()) { + const Record &R = *Class; OS << "DEF_ADD_DATA(" << R.getName() << ", {\n"; auto Code = R.getValue("Code")->getValue(); OS << Code->getAsUnquotedString() << "}\n)"; diff --git a/llvm/docs/TableGen/BackGuide.md b/llvm/docs/TableGen/BackGuide.md index bc1ce3c82c5834..a1abf291423eac 100644 --- a/llvm/docs/TableGen/BackGuide.md +++ b/llvm/docs/TableGen/BackGuide.md @@ -434,29 +434,24 @@ conventions used in the skeleton file. The `RecordKeeper` class provides two functions for getting the `Record` instances for classes defined in the TableGen files. -- `getClasses()` returns a `RecordMap` reference for all the classes. +- `getClasses()` returns all the class records, sorted by name. - `getClass(`*name*`)` returns a `Record` reference for the named class. If you need to iterate over all the class records: ```text -for (auto ClassPair : Records.getClasses()) { - Record *ClassRec = ClassPair.second.get(); +for (const Record *ClassRec : Records.getClasses()) { ... } ``` -`ClassPair.second` gets the class's `unique_ptr`, and then `.get()` gets the -class `Record` itself. - ## Getting Records The `RecordKeeper` class provides four functions for getting the `Record` instances for concrete records defined in the TableGen files. -- `getDefs()` returns a `RecordMap` reference for all the concrete - records. +- `getDefs()` returns all the concrete records, sorted by name. - `getDef(`*name*`)` returns a `Record` reference for the named concrete record. - `getAllDerivedDefinitions(`*classname*`)` returns a vector of diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h index d3e1b6ccbf5c96..3823d8dd4d91e9 100644 --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -23,6 +23,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" @@ -2043,7 +2044,7 @@ class Record { raw_ostream &operator<<(raw_ostream &OS, const Record &R); class RecordKeeper { - using RecordMap = std::map<std::string, std::unique_ptr<Record>, std::less<>>; + using RecordMap = StringMap<std::unique_ptr<Record>>; using GlobalMap = std::map<std::string, const Init *, std::less<>>; public: @@ -2056,25 +2057,25 @@ class RecordKeeper { /// Get the main TableGen input file's name. StringRef getInputFilename() const { return InputFilename; } - /// Get the map of classes. - const RecordMap &getClasses() const { return Classes; } + /// Get all classes, sorted by name. + ArrayRef<const Record *> getClasses() const; - /// Get the map of records (defs). - const RecordMap &getDefs() const { return Defs; } + /// Get all records (defs), sorted by name. + ArrayRef<const Record *> getDefs() const; /// Get the map of global variables. const GlobalMap &getGlobals() const { return ExtraGlobals; } /// Get the class with the specified name. const Record *getClass(StringRef Name) const { - auto I = Classes.find(Name); - return I == Classes.end() ? nullptr : I->second.get(); + auto It = Classes.find(Name); + return It == Classes.end() ? nullptr : It->second.get(); } /// Get the concrete record with the specified name. const Record *getDef(StringRef Name) const { - auto I = Defs.find(Name); - return I == Defs.end() ? nullptr : I->second.get(); + auto It = Defs.find(Name); + return It == Defs.end() ? nullptr : It->second.get(); } /// Get the \p Init value of the specified global variable. @@ -2090,14 +2091,13 @@ class RecordKeeper { } void addClass(std::unique_ptr<Record> R) { - bool Ins = - Classes.try_emplace(std::string(R->getName()), std::move(R)).second; + bool Ins = Classes.try_emplace(R->getName(), std::move(R)).second; (void)Ins; assert(Ins && "Class already exists"); } void addDef(std::unique_ptr<Record> R) { - bool Ins = Defs.try_emplace(std::string(R->getName()), std::move(R)).second; + bool Ins = Defs.try_emplace(R->getName(), std::move(R)).second; (void)Ins; assert(Ins && "Record already exists"); // Clear cache @@ -2145,6 +2145,8 @@ class RecordKeeper { std::string InputFilename; RecordMap Classes, Defs; + // Classes and Defs sorted by name, built on demand. + mutable std::vector<const Record *> SortedClasses, SortedDefs; mutable std::map<std::string, std::vector<const Record *>> Cache; GlobalMap ExtraGlobals; diff --git a/llvm/lib/TableGen/DetailedRecordsBackend.cpp b/llvm/lib/TableGen/DetailedRecordsBackend.cpp index b1152bf680c69a..fc79df45631fa7 100644 --- a/llvm/lib/TableGen/DetailedRecordsBackend.cpp +++ b/llvm/lib/TableGen/DetailedRecordsBackend.cpp @@ -81,10 +81,10 @@ void DetailedRecordsEmitter::printVariables(raw_ostream &OS) { // Print classes, including the template arguments, superclasses, and fields. void DetailedRecordsEmitter::printClasses(raw_ostream &OS) { - const auto &ClassList = Records.getClasses(); + ArrayRef<const Record *> ClassList = Records.getClasses(); printSectionHeading("Classes", ClassList.size(), OS); - for (const auto &[Name, Class] : ClassList) { + for (const Record *Class : ClassList) { OS << formatv("\n{0} |{1}|\n", Class->getNameInitAsString(), SrcMgr.getFormattedLocationNoOffset(Class->getLoc().front())); printTemplateArgs(*Class, OS); @@ -95,10 +95,10 @@ void DetailedRecordsEmitter::printClasses(raw_ostream &OS) { // Print the records, including the defm sequences, supercasses, and fields. void DetailedRecordsEmitter::printRecords(raw_ostream &OS) { - const auto &RecordList = Records.getDefs(); + ArrayRef<const Record *> RecordList = Records.getDefs(); printSectionHeading("Records", RecordList.size(), OS); - for (const auto &[DefName, Rec] : RecordList) { + for (const Record *Rec : RecordList) { std::string Name = Rec->getNameInitAsString(); OS << formatv("\n{0} |{1}|\n", Name.empty() ? "\"\"" : Name, SrcMgr.getFormattedLocationNoOffset(Rec->getLoc().front())); diff --git a/llvm/lib/TableGen/JSONBackend.cpp b/llvm/lib/TableGen/JSONBackend.cpp index 80c1d7fd1dc4dc..d9f93a38963274 100644 --- a/llvm/lib/TableGen/JSONBackend.cpp +++ b/llvm/lib/TableGen/JSONBackend.cpp @@ -127,11 +127,11 @@ void JSONEmitter::run(raw_ostream &OS) { // over the classes, invoking std::map::operator[] to default- // construct the array for each one. std::map<std::string, json::Array> InstanceLists; - for (const auto &[ClassName, ClassRec] : Records.getClasses()) + for (const Record *ClassRec : Records.getClasses()) InstanceLists.emplace(ClassRec->getNameInitAsString(), json::Array()); // Main iteration over the defs. - for (const auto &[DefName, Def] : Records.getDefs()) { + for (const Record *Def : Records.getDefs()) { const std::string Name = Def->getNameInitAsString(); json::Object Obj; diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index f456222b5ea788..b9870b8510a974 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -3165,11 +3165,11 @@ LLVM_DUMP_METHOD void RecordKeeper::dump() const { errs() << *this; } raw_ostream &llvm::operator<<(raw_ostream &OS, const RecordKeeper &RK) { OS << "------------- Classes -----------------\n"; - for (const auto &[_, C] : RK.getClasses()) + for (const Record *C : RK.getClasses()) OS << "class " << *C; OS << "------------- Defs -----------------\n"; - for (const auto &[_, D] : RK.getDefs()) + for (const Record *D : RK.getDefs()) OS << "def " << *D; return OS; } @@ -3190,10 +3190,35 @@ RecordKeeper::getAllDerivedDefinitions(StringRef ClassName) const { return Iter->second; } +// Return the records in Map sorted by name. Records are never removed, so +// Sorted is out of date if its size diff ers. +static ArrayRef<const Record *> +getSortedRecords(const StringMap<std::unique_ptr<Record>> &Map, + std::vector<const Record *> &Sorted) { + if (Sorted.size() != Map.size()) { + Sorted.clear(); + Sorted.reserve(Map.size()); + for (const auto &Entry : Map) + Sorted.push_back(Entry.second.get()); + llvm::sort(Sorted, [](const Record *LHS, const Record *RHS) { + return LHS->getName() < RHS->getName(); + }); + } + return Sorted; +} + +ArrayRef<const Record *> RecordKeeper::getClasses() const { + return getSortedRecords(Classes, SortedClasses); +} + +ArrayRef<const Record *> RecordKeeper::getDefs() const { + return getSortedRecords(Defs, SortedDefs); +} + std::vector<const Record *> RecordKeeper::getAllDerivedDefinitions(ArrayRef<StringRef> ClassNames) const { SmallVector<const Record *, 2> ClassRecs; - std::vector<const Record *> Defs; + std::vector<const Record *> Derived; assert(ClassNames.size() > 0 && "At least one class must be passed."); for (StringRef ClassName : ClassNames) { @@ -3203,14 +3228,15 @@ RecordKeeper::getAllDerivedDefinitions(ArrayRef<StringRef> ClassNames) const { ClassRecs.push_back(Class); } - for (const auto &OneDef : getDefs()) { - if (all_of(ClassRecs, [&OneDef](const Record *Class) { - return OneDef.second->isSubClassOf(Class); - })) - Defs.push_back(OneDef.second.get()); + // The iteration order of Defs doesn't matter since the result is sorted. + for (const auto &OneDef : Defs) { + const Record *Def = OneDef.second.get(); + if (all_of(ClassRecs, + [Def](const Record *Class) { return Def->isSubClassOf(Class); })) + Derived.push_back(Def); } - llvm::sort(Defs, LessRecord()); - return Defs; + llvm::sort(Derived, LessRecord()); + return Derived; } ArrayRef<const Record *> diff --git a/llvm/utils/TableGen/CTagsEmitter.cpp b/llvm/utils/TableGen/CTagsEmitter.cpp index 413d8f5dbcff07..b29eac9cf92fe3 100644 --- a/llvm/utils/TableGen/CTagsEmitter.cpp +++ b/llvm/utils/TableGen/CTagsEmitter.cpp @@ -69,18 +69,18 @@ SMLoc CTagsEmitter::locate(const Record *R) { } void CTagsEmitter::run(raw_ostream &OS) { - const auto &Classes = Records.getClasses(); - const auto &Defs = Records.getDefs(); + ArrayRef<const Record *> Classes = Records.getClasses(); + ArrayRef<const Record *> Defs = Records.getDefs(); std::vector<Tag> Tags; // Collect tags. Tags.reserve(Classes.size() + Defs.size()); - for (const auto &C : Classes) { - Tags.push_back(Tag(C.first, locate(C.second.get()))); - for (SMLoc FwdLoc : C.second->getForwardDeclarationLocs()) - Tags.push_back(Tag(C.first, FwdLoc)); + for (const Record *C : Classes) { + Tags.push_back(Tag(C->getName(), locate(C))); + for (SMLoc FwdLoc : C->getForwardDeclarationLocs()) + Tags.push_back(Tag(C->getName(), FwdLoc)); } - for (const auto &D : Defs) - Tags.push_back(Tag(D.first, locate(D.second.get()))); + for (const Record *D : Defs) + Tags.push_back(Tag(D->getName(), locate(D))); // Emit tags. llvm::sort(Tags); OS << "!_TAG_FILE_FORMAT\t1\t/original ctags format/\n"; diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp index 65aa86e33fba32..c43471b32911de 100644 --- a/llvm/utils/TableGen/SearchableTableEmitter.cpp +++ b/llvm/utils/TableGen/SearchableTableEmitter.cpp @@ -1008,8 +1008,7 @@ void SearchableTableEmitter::run(raw_ostream &OS) { // Translate legacy tables. const Record *SearchableTable = Records.getClass("SearchableTable"); - for (auto &NameRec : Records.getClasses()) { - const Record *Class = NameRec.second.get(); + for (const Record *Class : Records.getClasses()) { if (Class->getDirectSuperClasses().size() != 1 || !Class->isSubClassOf(SearchableTable)) continue; diff --git a/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp b/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp index 64e86f2a62073b..807a3ef5926d64 100644 --- a/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp +++ b/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp @@ -82,7 +82,7 @@ static bool findUse(const Init *field, const Init *deprecatedInit, } // Returns if there is a use of `deprecatedInit` in `record`. -static bool findUse(Record &record, const Init *deprecatedInit, +static bool findUse(const Record &record, const Init *deprecatedInit, llvm::DenseMap<const Init *, bool> &known) { return llvm::any_of(record.getValues(), [&](const RecordVal &val) { return findUse(val.getValue(), deprecatedInit, known); @@ -94,21 +94,21 @@ static void warnOfDeprecatedUses(const RecordKeeper &records) { // finds all uses of deprecated def. Deprecated defs are not expected to be // either numerous or long lived. bool deprecatedDefsFounds = false; - for (auto &it : records.getDefs()) { - const RecordVal *r = it.second->getValue("odsDeprecated"); + for (const Record *it : records.getDefs()) { + const RecordVal *r = it->getValue("odsDeprecated"); if (!r || !r->getValue()) continue; llvm::DenseMap<const Init *, bool> hasUse; if (auto *si = dyn_cast<StringInit>(r->getValue())) { - for (auto &jt : records.getDefs()) { + for (const Record *jt : records.getDefs()) { // Skip anonymous defs. - if (jt.second->isAnonymous()) + if (jt->isAnonymous()) continue; - if (findUse(*jt.second, it.second->getDefInit(), hasUse)) { - PrintWarning(jt.second->getLoc(), - "Using deprecated def `" + it.first + "`"); + if (findUse(*jt, it->getDefInit(), hasUse)) { + PrintWarning(jt->getLoc(), + "Using deprecated def `" + it->getName() + "`"); PrintNote(si->getAsUnquotedString()); deprecatedDefsFounds = true; } diff --git a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp index 8e67da90763d89..2dae2448fda306 100644 --- a/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp +++ b/mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp @@ -282,11 +282,9 @@ void TableGenIndex::initialize(const RecordKeeper &records) { if (!isDef) sym->references.push_back(refLoc); }; - auto classes = - llvm::make_pointee_range(llvm::make_second_range(records.getClasses())); - auto defs = - llvm::make_pointee_range(llvm::make_second_range(records.getDefs())); - for (const Record &def : llvm::concat<Record>(classes, defs)) { + auto classes = llvm::make_pointee_range(records.getClasses()); + auto defs = llvm::make_pointee_range(records.getDefs()); + for (const Record &def : llvm::concat<const Record>(classes, defs)) { auto *sym = getOrInsertDef(&def); insertRef(sym, sym->defLoc, /*isDef=*/true); diff --git a/mlir/tools/mlir-tblgen/OpGenHelpers.cpp b/mlir/tools/mlir-tblgen/OpGenHelpers.cpp index 44dbacf19fffd4..afc8272bc2a630 100644 --- a/mlir/tools/mlir-tblgen/OpGenHelpers.cpp +++ b/mlir/tools/mlir-tblgen/OpGenHelpers.cpp @@ -52,18 +52,16 @@ mlir::tblgen::getRequestedOpDefinitions(const RecordKeeper &records) { Regex includeRegex(opIncFilter), excludeRegex(opExcFilter); std::vector<const Record *> defs; - for (const auto &def : records.getDefs()) { - if (!def.second->isSubClassOf(classDef)) + for (const Record *def : records.getDefs()) { + if (!def->isSubClassOf(classDef)) continue; // Include if no include filter or include filter matches. - if (!opIncFilter.empty() && - !includeRegex.match(getOperationName(*def.second))) + if (!opIncFilter.empty() && !includeRegex.match(getOperationName(*def))) continue; // Unless there is an exclude filter and it matches. - if (!opExcFilter.empty() && - excludeRegex.match(getOperationName(*def.second))) + if (!opExcFilter.empty() && excludeRegex.match(getOperationName(*def))) continue; - defs.push_back(def.second.get()); + defs.push_back(def); } return defs; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
