Skip to content

Commit 137ba61

Browse files
Merge pull request #69400 from nate-chandler/irgen/note-implicitly-completed-metadata
[IRGen] Propagate locally-cached metadata completion to transitive dependencies.
2 parents a60395e + b8990b5 commit 137ba61

File tree

4 files changed

+512
-6
lines changed

4 files changed

+512
-6
lines changed

lib/IRGen/IRGenFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,8 @@ class IRGenFunction {
673673
MetadataResponse tryGetConcreteLocalTypeData(LocalTypeDataKey key,
674674
DynamicMetadataRequest request);
675675
void setUnscopedLocalTypeData(LocalTypeDataKey key, MetadataResponse value);
676-
void setScopedLocalTypeData(LocalTypeDataKey key, MetadataResponse value);
676+
void setScopedLocalTypeData(LocalTypeDataKey key, MetadataResponse value,
677+
bool mayEmitDebugInfo = true);
677678

678679
/// Given a concrete type metadata node, add all the local type data
679680
/// that we can reach from it.

lib/IRGen/LocalTypeData.cpp

Lines changed: 188 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/AST/IRGenOptions.h"
2929
#include "swift/AST/PackConformance.h"
3030
#include "swift/AST/ProtocolConformance.h"
31+
#include "swift/Basic/GraphNodeWorklist.h"
3132
#include "swift/SIL/SILModule.h"
3233

3334
using namespace swift;
@@ -304,6 +305,84 @@ LocalTypeDataCache::tryGet(IRGenFunction &IGF, LocalTypeDataKey key,
304305
llvm_unreachable("bad cache entry kind");
305306
}
306307

308+
LocalTypeDataCache::StateAdvancement LocalTypeDataCache::advanceStateInScope(
309+
IRGenFunction &IGF, LocalTypeDataKey key, MetadataState state) {
310+
// Use the caching key.
311+
key = key.getCachingKey();
312+
313+
auto iterator = Map.find(key);
314+
// There's no chain of entries, no no entry which could possibly be used.
315+
if (iterator == Map.end())
316+
return StateAdvancement::NoEntry;
317+
auto &chain = iterator->second;
318+
319+
// Scan the chain for entries with the appropriate relationship to the active
320+
// dominance scope, and "promote their state". Any entry whose state is
321+
// already at least as complete than `state` is unaffected, and results in
322+
// exiting early.
323+
//
324+
// There are two cases of interest:
325+
//
326+
// (1) DominancePoint(entry) dominates ActiveDominancePoint .
327+
// (2) DominancePoint(entry) is dominated by ActiveDominancePoint .
328+
//
329+
// For (1), a new cache entry is created at ActiveDominancePoint.
330+
// For (2), the state of the existing entry would be updated.
331+
//
332+
// Because of the order in which IRGen lowers, however, (2) can't actually
333+
// happen: metadata whose dominance point is dominated by
334+
// ActiveDominancePoint would not have been emitted yet.
335+
336+
// Find the best entry in the chain from which to produce a new entry.
337+
CacheEntry *best = nullptr;
338+
for (auto *link = chain.Root; link; link = link->getNext()) {
339+
// In case (1)?
340+
if (!IGF.isActiveDominancePointDominatedBy(link->DefinitionPoint))
341+
continue;
342+
343+
switch (link->getKind()) {
344+
case CacheEntry::Kind::Concrete: {
345+
auto entry = cast<ConcreteCacheEntry>(link);
346+
// If the entry is already as complete as `state`, it doesn't need to be
347+
// used to create a new entry. In fact, no new entry needs to be created
348+
// at all: this entry will be seen to be best if locally cached metadata
349+
// is requested later. Stop traversal and return.
350+
if (isAtLeast(entry->Value.getStaticLowerBoundOnState(), state))
351+
return StateAdvancement::AlreadyAtLeast;
352+
353+
// Any suitable concrete entry is equally ideal.
354+
best = entry;
355+
break;
356+
}
357+
case CacheEntry::Kind::Abstract: {
358+
// TODO: Consider the cost to materialize the abstract entry in order to
359+
// determine which is best.
360+
break;
361+
}
362+
}
363+
}
364+
365+
if (!best)
366+
return StateAdvancement::NoEntry;
367+
368+
switch (best->getKind()) {
369+
case CacheEntry::Kind::Concrete: {
370+
auto *entry = cast<ConcreteCacheEntry>(best);
371+
// Create a new entry at the ActiveDominancePoint.
372+
auto response =
373+
MetadataResponse::forBounded(entry->Value.getMetadata(), state);
374+
IGF.setScopedLocalTypeData(key, response,
375+
/*mayEmitDebugInfo=*/false);
376+
377+
return StateAdvancement::Advanced;
378+
}
379+
case CacheEntry::Kind::Abstract:
380+
// TODO: Advance abstract entries.
381+
return StateAdvancement::NoEntry;
382+
}
383+
llvm_unreachable("covered switch!?");
384+
}
385+
307386
MetadataResponse
308387
LocalTypeDataCache::AbstractCacheEntry::follow(IRGenFunction &IGF,
309388
AbstractSource &source,
@@ -381,10 +460,112 @@ IRGenFunction::setScopedLocalTypeMetadataForLayout(SILType type,
381460
setScopedLocalTypeData(key, response);
382461
}
383462

384-
void IRGenFunction::setScopedLocalTypeMetadata(CanType type,
385-
MetadataResponse response) {
463+
namespace {
464+
465+
void setScopedLocalTypeMetadataImpl(IRGenFunction &IGF, CanType type,
466+
MetadataResponse response) {
386467
auto key = LocalTypeDataKey(type, LocalTypeDataKind::forFormalTypeMetadata());
387-
setScopedLocalTypeData(key, response);
468+
IGF.setScopedLocalTypeData(key, response);
469+
}
470+
471+
/// Walks the types upon whose corresponding metadata records' completeness the
472+
/// completeness of \p rootTy's metadata record depends. For each such type,
473+
/// marks the corresponding locally cached metadata record, if any, complete.
474+
class TransitiveMetadataCompletion {
475+
IRGenFunction &IGF;
476+
LocalTypeDataCache &cache;
477+
CanType rootTy;
478+
GraphNodeWorklist<CanType, 4> worklist;
479+
480+
public:
481+
TransitiveMetadataCompletion(IRGenFunction &IGF, LocalTypeDataCache &cache,
482+
CanType rootTy)
483+
: IGF(IGF), cache(cache), rootTy(rootTy) {}
484+
485+
void complete();
486+
487+
private:
488+
/// Marks the metadata record currently locally cached corresponding to \p ty
489+
/// complete.
490+
///
491+
/// Returns whether \p ty's transitive metadata should be marked complete.
492+
bool visit(CanType ty) {
493+
// If it's the root type, it's already been marked complete, but we want to
494+
// mark its transitively dependent metadata as complete.
495+
if (ty == rootTy)
496+
return true;
497+
auto key = LocalTypeDataKey(ty, LocalTypeDataKind::forFormalTypeMetadata());
498+
// The metadata record was already marked complete. When that was done, the
499+
// records for types it has transitive completeness requirements on would
500+
// have been marked complete, if they had already been materialized.
501+
//
502+
// Such records may have been materialized since then in an abstract state,
503+
// but that is an unlikely case and scanning again would incur compile-time
504+
// overhead.
505+
if (cache.advanceStateInScope(IGF, key, MetadataState::Complete) ==
506+
LocalTypeDataCache::StateAdvancement::AlreadyAtLeast)
507+
return false;
508+
return true;
509+
}
510+
};
511+
512+
void TransitiveMetadataCompletion::complete() {
513+
worklist.initialize(rootTy);
514+
515+
while (auto ty = worklist.pop()) {
516+
if (!visit(ty)) {
517+
// The transitively dependent metadata of `ty` doesn't need to be marked
518+
// complete.
519+
continue;
520+
}
521+
522+
// Walk into every type that `ty` has transitive completeness requirements
523+
// on and mark each one transitively complete.
524+
//
525+
// This should mirror findAnyTransitiveMetadata: every type whose metadata
526+
// is visited (i.e. has predicate called on it) by that function should be
527+
// pushed onto the worklist.
528+
if (auto ct = dyn_cast<ClassType>(ty)) {
529+
if (auto rawSuperTy = ct->getSuperclass()) {
530+
auto superTy = rawSuperTy->getCanonicalType();
531+
worklist.insert(superTy);
532+
}
533+
} else if (auto bgt = dyn_cast<BoundGenericType>(ty)) {
534+
if (auto ct = dyn_cast<BoundGenericClassType>(bgt)) {
535+
if (auto rawSuperTy = ct->getSuperclass()) {
536+
auto superTy = rawSuperTy->getCanonicalType();
537+
worklist.insert(superTy);
538+
}
539+
}
540+
for (auto arg : bgt->getExpandedGenericArgs()) {
541+
auto childTy = arg->getCanonicalType();
542+
worklist.insert(childTy);
543+
}
544+
} else if (auto tt = dyn_cast<TupleType>(ty)) {
545+
for (auto elt : tt.getElementTypes()) {
546+
worklist.insert(elt);
547+
}
548+
}
549+
}
550+
}
551+
552+
} // end anonymous namespace
553+
554+
void IRGenFunction::setScopedLocalTypeMetadata(CanType rootTy,
555+
MetadataResponse response) {
556+
setScopedLocalTypeMetadataImpl(*this, rootTy, response);
557+
558+
if (response.getStaticLowerBoundOnState() != MetadataState::Complete)
559+
return;
560+
561+
// If the metadata record is complete, then it is _transitively_ complete.
562+
// So every metadata record that it has transitive completeness requirements
563+
// on must also be complete.
564+
//
565+
// Mark all such already materialized metadata that the given type has
566+
// transitive completeness requirements on as complete.
567+
TransitiveMetadataCompletion(*this, getOrCreateLocalTypeData(), rootTy)
568+
.complete();
388569
}
389570

390571
void IRGenFunction::setScopedLocalTypeData(CanType type,
@@ -404,8 +585,10 @@ void IRGenFunction::setScopedLocalTypeDataForLayout(SILType type,
404585
}
405586

406587
void IRGenFunction::setScopedLocalTypeData(LocalTypeDataKey key,
407-
MetadataResponse value) {
408-
maybeEmitDebugInfoForLocalTypeData(*this, key, value);
588+
MetadataResponse value,
589+
bool mayEmitDebugInfo) {
590+
if (mayEmitDebugInfo)
591+
maybeEmitDebugInfoForLocalTypeData(*this, key, value);
409592

410593
// Register with the active ConditionalDominanceScope if necessary.
411594
bool isConditional = isConditionalDominancePoint();

lib/IRGen/LocalTypeData.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,22 @@ class LocalTypeDataCache {
271271
MetadataResponse tryGet(IRGenFunction &IGF, LocalTypeDataKey key,
272272
bool allowAbstract, DynamicMetadataRequest request);
273273

274+
/// Whether the cached state was advanced or otherwise why not.
275+
enum class StateAdvancement {
276+
/// No entry whose state could be advanced was found.
277+
NoEntry,
278+
/// An entry was found whose state was already advanced at least as far as
279+
/// the indicated state.
280+
AlreadyAtLeast,
281+
/// The state was advanced.
282+
Advanced,
283+
};
284+
285+
/// Advances the state cached for \p key to \p state within the active
286+
/// dominance scope.
287+
StateAdvancement advanceStateInScope(IRGenFunction &IGF, LocalTypeDataKey key,
288+
MetadataState state);
289+
274290
/// Add a new concrete entry to the cache at the given definition point.
275291
void addConcrete(DominancePoint point, bool isConditional,
276292
LocalTypeDataKey key, MetadataResponse value) {

0 commit comments

Comments
 (0)