[llvm-branch-commits] [llvm] TableGen: Cache resolved class instantiations (PR #231091)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Oct 12 00:43:31 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: Matt Arsenault (arsenm)
<details>
<summary>Changes</summary>
The same class instantiation is often referenced many times in one
record, and each reference was resolved from scratch along with
everything nested in its arguments. Cache the results in MapResolver and
RecordResolver, and share them with any TrackUnresolvedResolver wrapping
those.
Gigainstructions executed by llvm-tblgen --null-backend
before after
AMDGPU.td 71.75 18.22 -74.6%
X86.td 6.10 6.15 +0.8%
AArch64.td 3.28 3.32 +1.3%
RISCV.td 8.18 8.26 +1.0%
Co-authored-by: Claude Opus 5.5 <noreply@<!-- -->anthropic.com>
---
<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
---
Full diff: https://github.com/llvm/llvm-project/pull/231091.diff
2 Files Affected:
- (modified) llvm/include/llvm/TableGen/Record.h (+138-3)
- (modified) llvm/lib/TableGen/Record.cpp (+14-4)
``````````diff
diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index 01ad1287dccc1..f69605b907555 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -2270,7 +2270,33 @@ class Resolver {
// should only happen when this is true.
bool isFinal() const { return IsFinal; }
- void setFinal(bool Final) { IsFinal = Final; }
+ void setFinal(bool Final) {
+ IsFinal = Final;
+ clearCachedResults();
+ }
+
+ /// Return the cached result of resolving \p I, or nullptr.
+ virtual const Init *getCachedResult(const Init *I) { return nullptr; }
+
+ /// Cache the result of resolving \p I.
+ virtual void cacheResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) {}
+
+ /// Cache lookup for TrackUnresolvedResolvers wrapping this resolver.
+ virtual const Init *getCachedTrackedResult(const Init *I,
+ bool &FoundUnresolved) const {
+ return nullptr;
+ }
+
+ /// Cache insertion for TrackUnresolvedResolvers wrapping this resolver.
+ virtual void cacheTrackedResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) {}
+
+ /// Called when a cached result has unresolved references.
+ virtual void noteUnresolved() {}
+
+protected:
+ virtual void clearCachedResults() {}
};
/// Resolve arbitrary mappings.
@@ -2284,11 +2310,20 @@ class MapResolver final : public Resolver {
};
DenseMap<const Init *, MappedValue> Map;
+ DenseMap<const Init *, const Init *> ResultCache;
+ DenseMap<const Init *, std::pair<const Init *, bool>> TrackedResultCache;
+ // Number of mapped values being resolved. Each is removed from Map while it
+ // is resolved to prevent infinite recursion, so results computed meanwhile
+ // may be incomplete and are not cached.
+ unsigned NumInProgress = 0;
public:
explicit MapResolver(const Record *CurRec = nullptr) : Resolver(CurRec) {}
- void set(const Init *Key, const Init *Value) { Map[Key] = {Value, false}; }
+ void set(const Init *Key, const Init *Value) {
+ Map[Key] = {Value, false};
+ clearCachedResults();
+ }
bool isComplete(Init *VarName) const {
auto It = Map.find(VarName);
@@ -2297,22 +2332,91 @@ class MapResolver final : public Resolver {
}
const Init *resolve(const Init *VarName) override;
+
+ const Init *getCachedResult(const Init *I) override {
+ return NumInProgress ? nullptr : ResultCache.lookup(I);
+ }
+
+ void cacheResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) override {
+ if (!NumInProgress)
+ ResultCache[I] = Result;
+ }
+
+ const Init *getCachedTrackedResult(const Init *I,
+ bool &FoundUnresolved) const override {
+ if (NumInProgress)
+ return nullptr;
+ auto [Result, Unresolved] = TrackedResultCache.lookup(I);
+ FoundUnresolved = Unresolved;
+ return Result;
+ }
+
+ void cacheTrackedResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) override {
+ if (!NumInProgress)
+ TrackedResultCache[I] = {Result, FoundUnresolved};
+ }
+
+protected:
+ void clearCachedResults() override {
+ ResultCache.clear();
+ TrackedResultCache.clear();
+ }
};
/// Resolve all variables from a record except for unset variables.
class RecordResolver final : public Resolver {
DenseMap<const Init *, const Init *> Cache;
+ DenseMap<const Init *, const Init *> ResultCache;
+ DenseMap<const Init *, std::pair<const Init *, bool>> TrackedResultCache;
SmallVector<const Init *, 4> Stack;
const Init *Name = nullptr;
public:
explicit RecordResolver(const Record &R) : Resolver(&R) {}
- void setName(const Init *NewName) { Name = NewName; }
+ void setName(const Init *NewName) {
+ Name = NewName;
+ clearCachedResults();
+ }
const Init *resolve(const Init *VarName) override;
bool keepUnsetBits() const override { return true; }
+
+ // Variables on the stack resolve to nullptr to prevent infinite recursion,
+ // so results computed meanwhile may be incomplete and are not cached.
+ const Init *getCachedResult(const Init *I) override {
+ return Stack.empty() ? ResultCache.lookup(I) : nullptr;
+ }
+
+ void cacheResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) override {
+ if (Stack.empty())
+ ResultCache[I] = Result;
+ }
+
+ const Init *getCachedTrackedResult(const Init *I,
+ bool &FoundUnresolved) const override {
+ if (!Stack.empty())
+ return nullptr;
+ auto [Result, Unresolved] = TrackedResultCache.lookup(I);
+ FoundUnresolved = Unresolved;
+ return Result;
+ }
+
+ void cacheTrackedResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) override {
+ if (Stack.empty())
+ TrackedResultCache[I] = {Result, FoundUnresolved};
+ }
+
+protected:
+ void clearCachedResults() override {
+ ResultCache.clear();
+ TrackedResultCache.clear();
+ }
};
/// Delegate resolving to a sub-resolver, but shadow some variable names.
@@ -2348,6 +2452,37 @@ class TrackUnresolvedResolver final : public Resolver {
bool foundUnresolved() const { return FoundUnresolved; }
const Init *resolve(const Init *VarName) override;
+
+ // Tracking doesn't change results, so share the underlying resolver's cache.
+ const Init *getCachedResult(const Init *I) override {
+ bool Unresolved = false;
+ const Init *Result = getCachedTrackedResult(I, Unresolved);
+ if (Result && Unresolved)
+ noteUnresolved();
+ return Result;
+ }
+
+ void cacheResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) override {
+ cacheTrackedResult(I, Result, FoundUnresolved);
+ }
+
+ const Init *getCachedTrackedResult(const Init *I,
+ bool &FoundUnresolved) const override {
+ return R ? R->getCachedTrackedResult(I, FoundUnresolved) : nullptr;
+ }
+
+ void cacheTrackedResult(const Init *I, const Init *Result,
+ bool FoundUnresolved) override {
+ if (R)
+ R->cacheTrackedResult(I, Result, FoundUnresolved);
+ }
+
+ void noteUnresolved() override {
+ FoundUnresolved = true;
+ if (R)
+ R->noteUnresolved();
+ }
};
/// Do not resolve anything, but keep track of whether a given variable was
diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index 9903e05511c81..fd9c0e89a8ec8 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -2420,6 +2420,11 @@ const DefInit *VarDefInit::instantiate() {
}
const Init *VarDefInit::resolveReferences(Resolver &R) const {
+ // The same instantiation is often referenced many times, and its arguments
+ // can be expensive to resolve.
+ if (const Init *Cached = R.getCachedResult(this))
+ return Cached;
+
TrackUnresolvedResolver UR(&R);
bool Changed = false;
SmallVector<const ArgumentInit *, 8> NewArgs;
@@ -2431,13 +2436,16 @@ const Init *VarDefInit::resolveReferences(Resolver &R) const {
Changed |= NewArg != Arg;
}
+ const Init *Result = this;
if (Changed) {
auto *New = VarDefInit::get(Loc, Class, NewArgs);
- if (!UR.foundUnresolved())
- return const_cast<VarDefInit *>(New)->instantiate();
- return New;
+ if (UR.foundUnresolved())
+ Result = New;
+ else
+ Result = const_cast<VarDefInit *>(New)->instantiate();
}
- return this;
+ R.cacheResult(this, Result, UR.foundUnresolved());
+ return Result;
}
const Init *VarDefInit::Fold() const {
@@ -3223,7 +3231,9 @@ const Init *MapResolver::resolve(const Init *VarName) {
// Resolve mutual references among the mapped variables, but prevent
// infinite recursion.
Map.erase(It);
+ ++NumInProgress;
I = I->resolveReferences(*this);
+ --NumInProgress;
Map[VarName] = {I, true};
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/231091
More information about the llvm-branch-commits
mailing list