Skip to content

Commit 8a5c02d

Browse files
committed
simplify away cache with holes
1 parent 513718d commit 8a5c02d

File tree

3 files changed

+59
-238
lines changed

3 files changed

+59
-238
lines changed

mlir/lib/Target/LLVMIR/DebugImporter.cpp

Lines changed: 25 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -319,139 +319,8 @@ getRecSelfConstructor(llvm::DINode *node) {
319319
.Default(CtorType());
320320
}
321321

322-
/// An iterative attribute replacer that also handles pruning recursive types.
323-
/// - Recurses down the attribute tree while replacing attributes based on the
324-
/// provided replacement map.
325-
/// - Keeps track of the currently open recursive declarations, and upon
326-
/// encountering a duplicate declaration, replace with a self-reference.
327-
static Attribute replaceAndPruneRecursiveTypes(
328-
Attribute baseNode,
329-
const DenseMap<DIRecursiveTypeAttrInterface, DINodeAttr> &mapping) {
330-
331-
struct ReplacementState {
332-
// The attribute being replaced.
333-
Attribute node;
334-
// The nested elements of `node`.
335-
SmallVector<Attribute> attrs;
336-
SmallVector<Type> types;
337-
// The current attr being walked on (index into `attrs`).
338-
size_t attrIndex;
339-
// Whether or not any attr was replaced.
340-
bool changed;
341-
342-
void replaceCurrAttr(Attribute attr) {
343-
Attribute &target = attrs[attrIndex];
344-
if (attr != target) {
345-
changed = true;
346-
target = attr;
347-
}
348-
}
349-
};
350-
351-
// Every iteration, perform replacement on the attribute at `attrIndex` at the
352-
// top of `workStack`.
353-
// If `attrIndex` reaches past the size of `attrs`, replace `node` with
354-
// `attrs` & `types`, and pop off a stack frame and assign the replacement to
355-
// the attr being replaced on the previous stack frame.
356-
SmallVector<ReplacementState> workStack;
357-
workStack.push_back({nullptr, {baseNode}, {}, 0, false});
358-
359-
// Replacement cache that remembers the context in which the cache is valid.
360-
// All unboundRecIds must be in openDecls at time of lookup.
361-
struct CacheWithContext {
362-
DenseSet<DistinctAttr> unboundRecIds;
363-
Attribute entry;
364-
};
365-
DenseMap<Attribute, CacheWithContext> replacementCache;
366-
367-
DenseSet<DistinctAttr> openDecls;
368-
while (workStack.size() > 1 || workStack.back().attrIndex == 0) {
369-
ReplacementState &state = workStack.back();
370-
371-
// Check for popping condition.
372-
if (state.attrIndex == state.attrs.size()) {
373-
Attribute result = state.node;
374-
if (state.changed)
375-
result = result.replaceImmediateSubElements(state.attrs, state.types);
376-
377-
// Reset context.
378-
if (auto recType = dyn_cast<DIRecursiveTypeAttrInterface>(state.node))
379-
if (DistinctAttr recId = recType.getRecId())
380-
openDecls.erase(recId);
381-
382-
replacementCache[state.node] = CacheWithContext{openDecls, result};
383-
384-
workStack.pop_back();
385-
ReplacementState &prevState = workStack.back();
386-
prevState.replaceCurrAttr(result);
387-
++prevState.attrIndex;
388-
continue;
389-
}
390-
391-
Attribute node = state.attrs[state.attrIndex];
392-
393-
// Lookup in cache first.
394-
if (auto it = replacementCache.find(node); it != replacementCache.end()) {
395-
// If all the requried recIds are open decls, use cache.
396-
if (llvm::set_is_subset(it->second.unboundRecIds, openDecls)) {
397-
state.replaceCurrAttr(it->second.entry);
398-
++state.attrIndex;
399-
continue;
400-
}
401-
402-
// Otherwise, the cache entry is stale and can be removed now.
403-
replacementCache.erase(it);
404-
}
405-
406-
if (auto recType = dyn_cast<DIRecursiveTypeAttrInterface>(node)) {
407-
if (DistinctAttr recId = recType.getRecId()) {
408-
if (recType.isRecSelf()) {
409-
// Replace selfRef based on the provided mapping and re-walk from the
410-
// replacement node (do not increment attrIndex).
411-
if (DINodeAttr replacement = mapping.lookup(recType)) {
412-
state.replaceCurrAttr(replacement);
413-
continue;
414-
}
415-
416-
// Otherwise, nothing to do. Advance to next attr.
417-
++state.attrIndex;
418-
continue;
419-
}
420-
421-
// Configure context.
422-
auto [_, inserted] = openDecls.insert(recId);
423-
if (!inserted) {
424-
// This is a nested decl. Replace with recSelf. Nothing more to do.
425-
state.replaceCurrAttr(recType.getRecSelf(recId));
426-
++state.attrIndex;
427-
continue;
428-
}
429-
}
430-
}
431-
432-
// Recurse into this node.
433-
workStack.push_back({node, {}, {}, 0, false});
434-
ReplacementState &newState = workStack.back();
435-
node.walkImmediateSubElements(
436-
[&newState](Attribute attr) { newState.attrs.push_back(attr); },
437-
[&newState](Type type) { newState.types.push_back(type); });
438-
};
439-
440-
return workStack.back().attrs.front();
441-
}
442-
443322
DINodeAttr DebugImporter::RecursionPruner::pruneOrPushTranslationStack(
444323
llvm::DINode *node) {
445-
// Lookup the cache first.
446-
auto [result, unboundSelfRefs] = lookup(node);
447-
if (result) {
448-
// Need to inject unbound self-refs into the previous layer.
449-
if (!unboundSelfRefs.empty())
450-
translationStack.back().second.unboundSelfRefs.insert(
451-
unboundSelfRefs.begin(), unboundSelfRefs.end());
452-
return result;
453-
}
454-
455324
// If the node type is capable of being recursive, check if it's seen
456325
// before.
457326
auto recSelfCtor = getRecSelfConstructor(node);
@@ -465,7 +334,11 @@ DINodeAttr DebugImporter::RecursionPruner::pruneOrPushTranslationStack(
465334
// a different self-reference. Use that if possible.
466335
DIRecursiveTypeAttrInterface recSelf = iter->second.recSelf;
467336
if (!recSelf) {
468-
DistinctAttr recId = DistinctAttr::create(UnitAttr::get(context));
337+
DistinctAttr recId = nodeToRecId.lookup(node);
338+
if (!recId) {
339+
recId = DistinctAttr::create(UnitAttr::get(context));
340+
nodeToRecId[node] = recId;
341+
}
469342
recSelf = recSelfCtor(recId);
470343
iter->second.recSelf = recSelf;
471344
}
@@ -474,7 +347,8 @@ DINodeAttr DebugImporter::RecursionPruner::pruneOrPushTranslationStack(
474347
return cast<DINodeAttr>(recSelf);
475348
}
476349
}
477-
return nullptr;
350+
351+
return lookup(node);
478352
}
479353

480354
std::pair<DINodeAttr, bool>
@@ -493,39 +367,18 @@ DebugImporter::RecursionPruner::finalizeTranslation(llvm::DINode *node,
493367
if (DIRecursiveTypeAttrInterface recSelf = state.recSelf) {
494368
auto recType = cast<DIRecursiveTypeAttrInterface>(result);
495369
result = cast<DINodeAttr>(recType.withRecId(recSelf.getRecId()));
496-
497370
// Remove this recSelf from the set of unbound selfRefs.
498371
state.unboundSelfRefs.erase(recSelf);
499-
500-
// Insert the newly resolved recursive type into the cache entries that
501-
// rely on it.
502-
// Only need to look at the caches at this level.
503-
uint64_t numRemaining = state.cacheSize;
504-
for (CachedTranslation &cacheEntry :
505-
llvm::make_second_range(llvm::reverse(cache))) {
506-
if (numRemaining == 0)
507-
break;
508-
--numRemaining;
509-
510-
if (auto refIter = cacheEntry.pendingReplacements.find(recSelf);
511-
refIter != cacheEntry.pendingReplacements.end())
512-
refIter->second = result;
513-
}
514372
}
515373

516-
// Insert the current result into the cache.
517-
state.cacheSize++;
518-
auto [iter, inserted] = cache.try_emplace(node);
519-
assert(inserted && "invalid state: caching the same DINode twice");
520-
iter->second.attr = result;
521-
522-
// If this node had any unbound self-refs free when it is registered into
523-
// the cache, set up replacement placeholders: This result will need these
524-
// unbound self-refs to be replaced before being used.
525-
for (DIRecursiveTypeAttrInterface selfRef : state.unboundSelfRefs)
526-
iter->second.pendingReplacements.try_emplace(selfRef, nullptr);
527-
528-
return {result, state.unboundSelfRefs.empty()};
374+
// Insert the result into our internal cache if it's not self-contained.
375+
if (!state.unboundSelfRefs.empty()) {
376+
auto [_, inserted] = dependentCache.try_emplace(
377+
node, DependentTranslation{result, state.unboundSelfRefs});
378+
assert(inserted && "invalid state: caching the same DINode twice");
379+
return {result, false};
380+
}
381+
return {result, true};
529382
}
530383

531384
void DebugImporter::RecursionPruner::popTranslationStack(llvm::DINode *node) {
@@ -540,56 +393,30 @@ void DebugImporter::RecursionPruner::popTranslationStack(llvm::DINode *node) {
540393
if (translationStack.size() == 1) {
541394
assert(currLayerState.unboundSelfRefs.empty() &&
542395
"internal error: unbound recursive self reference at top level.");
543-
assert(currLayerState.cacheSize == cache.size() &&
544-
"internal error: inconsistent cache size");
545396
translationStack.pop_back();
546-
cache.clear();
547397
return;
548398
}
549399

550400
// Copy unboundSelfRefs down to the previous level.
551401
TranslationState &nextLayerState = (++translationStack.rbegin())->second;
552402
nextLayerState.unboundSelfRefs.insert(currLayerState.unboundSelfRefs.begin(),
553403
currLayerState.unboundSelfRefs.end());
554-
555-
// The current layer cache is now considered part of the lower layer cache.
556-
nextLayerState.cacheSize += currLayerState.cacheSize;
557-
558-
// Finally pop off this layer when all bookkeeping is done.
559404
translationStack.pop_back();
560405
}
561406

562-
std::pair<DINodeAttr, DenseSet<DIRecursiveTypeAttrInterface>>
563-
DebugImporter::RecursionPruner::lookup(llvm::DINode *node) {
564-
auto cacheIter = cache.find(node);
565-
if (cacheIter == cache.end())
407+
DINodeAttr DebugImporter::RecursionPruner::lookup(llvm::DINode *node) {
408+
auto cacheIter = dependentCache.find(node);
409+
if (cacheIter == dependentCache.end())
566410
return {};
567411

568-
CachedTranslation &entry = cacheIter->second;
569-
570-
if (entry.pendingReplacements.empty())
571-
return std::make_pair(entry.attr, DenseSet<DIRecursiveTypeAttrInterface>{});
572-
573-
Attribute replacedAttr =
574-
replaceAndPruneRecursiveTypes(entry.attr, entry.pendingReplacements);
575-
DINodeAttr result = cast<DINodeAttr>(replacedAttr);
576-
577-
// Update cache entry to save replaced version and remove already-applied
578-
// replacements.
579-
entry.attr = result;
580-
DenseSet<DIRecursiveTypeAttrInterface> unboundRefs;
581-
DenseSet<DIRecursiveTypeAttrInterface> boundRefs;
582-
for (auto [refSelf, replacement] : entry.pendingReplacements) {
583-
if (replacement)
584-
boundRefs.insert(refSelf);
585-
else
586-
unboundRefs.insert(refSelf);
587-
}
588-
589-
for (DIRecursiveTypeAttrInterface ref : boundRefs)
590-
entry.pendingReplacements.erase(ref);
412+
DependentTranslation &entry = cacheIter->second;
413+
if (llvm::set_is_subset(entry.unboundSelfRefs,
414+
translationStack.back().second.unboundSelfRefs))
415+
return entry.attr;
591416

592-
return std::make_pair(result, unboundRefs);
417+
// Stale cache entry.
418+
dependentCache.erase(cacheIter);
419+
return {};
593420
}
594421

595422
//===----------------------------------------------------------------------===//

mlir/lib/Target/LLVMIR/DebugImporter.h

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,11 @@ class DebugImporter {
116116
public:
117117
RecursionPruner(MLIRContext *context) : context(context) {}
118118

119-
/// If this node was previously cached, returns the cached result.
120119
/// If this node is a recursive instance that was previously seen, returns a
121-
/// self-reference.
122-
/// Otherwise, returns null attr, and a translation stack frame is created
123-
/// for this node. Expects `finalizeTranslation` & `popTranslationStack`
124-
/// to be called on this node later.
120+
/// self-reference. If this node was previously cached, returns the cached
121+
/// result. Otherwise, returns null attr, and a translation stack frame is
122+
/// created for this node. Expects `finalizeTranslation` &
123+
/// `popTranslationStack` to be called on this node later.
125124
DINodeAttr pruneOrPushTranslationStack(llvm::DINode *node);
126125

127126
/// Register the translated result of `node`. Returns the finalized result
@@ -135,33 +134,27 @@ class DebugImporter {
135134
void popTranslationStack(llvm::DINode *node);
136135

137136
private:
138-
/// Returns the cached result (if exists) with all known replacements
139-
/// applied. Also returns the set of unbound self-refs that are unresolved
140-
/// in this cached result. The cache entry will also be updated with the
141-
/// replaced result and with the applied replacements removed from the
142-
/// pendingReplacements map.
143-
std::pair<DINodeAttr, DenseSet<DIRecursiveTypeAttrInterface>>
144-
lookup(llvm::DINode *node);
137+
/// Returns the cached result (if exists) or null.
138+
/// The cache entry will be removed if not all of its dependent self-refs
139+
/// exists.
140+
DINodeAttr lookup(llvm::DINode *node);
145141

146142
MLIRContext *context;
147143

148-
/// A lazy cached translation that contains the translated attribute as well
149-
/// as any unbound self-references that need to be replaced at lookup.
150-
struct CachedTranslation {
144+
/// A cached translation that contains the translated attribute as well
145+
/// as any unbound self-references that it depends on.
146+
struct DependentTranslation {
151147
/// The translated attr. May contain unbound self-references for other
152148
/// recursive attrs.
153149
DINodeAttr attr;
154-
/// The pending replacements that need to be run on this `attr` before it
155-
/// can be used.
156-
/// Each key is a recursive self-ref, and each value is a recursive decl
157-
/// that may contain additional unbound self-refs that need to be
158-
/// replaced. Each replacement will be applied at most once. Once a
159-
/// replacement is applied, the cached `attr` will be updated, and the
160-
/// replacement will be removed from this map.
161-
DenseMap<DIRecursiveTypeAttrInterface, DINodeAttr> pendingReplacements;
150+
/// The set of unbound self-refs that this cached entry refers to. All
151+
/// these self-refs must exist for the cached entry to be valid.
152+
DenseSet<DIRecursiveTypeAttrInterface> unboundSelfRefs;
162153
};
163154
/// A mapping between LLVM debug metadata and the corresponding attribute.
164-
llvm::MapVector<llvm::DINode *, CachedTranslation> cache;
155+
/// Only contains those with unboundSelfRefs. Fully self-contained attrs
156+
/// will be cached by the outer main translator.
157+
DenseMap<llvm::DINode *, DependentTranslation> dependentCache;
165158

166159
/// Each potentially recursive node will have a TranslationState pushed onto
167160
/// the `translationStack` to keep track of whether this node is actually
@@ -171,9 +164,6 @@ class DebugImporter {
171164
/// instance of itself is seen while translating it). Null if this node
172165
/// has not been seen again deeper in the translation stack.
173166
DIRecursiveTypeAttrInterface recSelf;
174-
/// The number of cache entries belonging to this layer of the translation
175-
/// stack. This corresponds to the last `cacheSize` entries of `cache`.
176-
uint64_t cacheSize = 0;
177167
/// All the unbound recursive self references in this layer of the
178168
/// translation stack.
179169
DenseSet<DIRecursiveTypeAttrInterface> unboundSelfRefs;
@@ -184,6 +174,11 @@ class DebugImporter {
184174
/// later when the stack is deeper, the node is recursive, and its
185175
/// TranslationState is assigned a recSelf.
186176
llvm::MapVector<llvm::DINode *, TranslationState> translationStack;
177+
178+
/// A mapping between DINodes that are recursive, and their assigned recId.
179+
/// This is kept so that repeated occurrences of the same node can reuse the
180+
/// same ID and be deduplicated.
181+
DenseMap<llvm::DINode *, DistinctAttr> nodeToRecId;
187182
};
188183
RecursionPruner recursionPruner;
189184

0 commit comments

Comments
 (0)