Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/IRGen/IRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,8 @@ class IRGenFunction {
MetadataResponse tryGetConcreteLocalTypeData(LocalTypeDataKey key,
DynamicMetadataRequest request);
void setUnscopedLocalTypeData(LocalTypeDataKey key, MetadataResponse value);
void setScopedLocalTypeData(LocalTypeDataKey key, MetadataResponse value);
void setScopedLocalTypeData(LocalTypeDataKey key, MetadataResponse value,
bool mayEmitDebugInfo = true);

/// Given a concrete type metadata node, add all the local type data
/// that we can reach from it.
Expand Down
193 changes: 188 additions & 5 deletions lib/IRGen/LocalTypeData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/PackConformance.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/Basic/GraphNodeWorklist.h"
#include "swift/SIL/SILModule.h"

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

LocalTypeDataCache::StateAdvancement LocalTypeDataCache::advanceStateInScope(
IRGenFunction &IGF, LocalTypeDataKey key, MetadataState state) {
// Use the caching key.
key = key.getCachingKey();

auto iterator = Map.find(key);
// There's no chain of entries, no no entry which could possibly be used.
if (iterator == Map.end())
return StateAdvancement::NoEntry;
auto &chain = iterator->second;

// Scan the chain for entries with the appropriate relationship to the active
// dominance scope, and "promote their state". Any entry whose state is
// already at least as complete than `state` is unaffected, and results in
// exiting early.
//
// There are two cases of interest:
//
// (1) DominancePoint(entry) dominates ActiveDominancePoint .
// (2) DominancePoint(entry) is dominated by ActiveDominancePoint .
//
// For (1), a new cache entry is created at ActiveDominancePoint.
// For (2), the state of the existing entry would be updated.
//
// Because of the order in which IRGen lowers, however, (2) can't actually
// happen: metadata whose dominance point is dominated by
// ActiveDominancePoint would not have been emitted yet.

// Find the best entry in the chain from which to produce a new entry.
CacheEntry *best = nullptr;
for (auto *link = chain.Root; link; link = link->getNext()) {
// In case (1)?
if (!IGF.isActiveDominancePointDominatedBy(link->DefinitionPoint))
continue;

switch (link->getKind()) {
case CacheEntry::Kind::Concrete: {
auto entry = cast<ConcreteCacheEntry>(link);
// If the entry is already as complete as `state`, it doesn't need to be
// used to create a new entry. In fact, no new entry needs to be created
// at all: this entry will be seen to be best if locally cached metadata
// is requested later. Stop traversal and return.
if (isAtLeast(entry->Value.getStaticLowerBoundOnState(), state))
return StateAdvancement::AlreadyAtLeast;

// Any suitable concrete entry is equally ideal.
best = entry;
break;
}
case CacheEntry::Kind::Abstract: {
// TODO: Consider the cost to materialize the abstract entry in order to
// determine which is best.
break;
}
}
}

if (!best)
return StateAdvancement::NoEntry;

switch (best->getKind()) {
case CacheEntry::Kind::Concrete: {
auto *entry = cast<ConcreteCacheEntry>(best);
// Create a new entry at the ActiveDominancePoint.
auto response =
MetadataResponse::forBounded(entry->Value.getMetadata(), state);
IGF.setScopedLocalTypeData(key, response,
/*mayEmitDebugInfo=*/false);

return StateAdvancement::Advanced;
}
case CacheEntry::Kind::Abstract:
// TODO: Advance abstract entries.
return StateAdvancement::NoEntry;
}
llvm_unreachable("covered switch!?");
}

MetadataResponse
LocalTypeDataCache::AbstractCacheEntry::follow(IRGenFunction &IGF,
AbstractSource &source,
Expand Down Expand Up @@ -381,10 +460,112 @@ IRGenFunction::setScopedLocalTypeMetadataForLayout(SILType type,
setScopedLocalTypeData(key, response);
}

void IRGenFunction::setScopedLocalTypeMetadata(CanType type,
MetadataResponse response) {
namespace {

void setScopedLocalTypeMetadataImpl(IRGenFunction &IGF, CanType type,
MetadataResponse response) {
auto key = LocalTypeDataKey(type, LocalTypeDataKind::forFormalTypeMetadata());
setScopedLocalTypeData(key, response);
IGF.setScopedLocalTypeData(key, response);
}

/// Walks the types upon whose corresponding metadata records' completeness the
/// completeness of \p rootTy's metadata record depends. For each such type,
/// marks the corresponding locally cached metadata record, if any, complete.
class TransitiveMetadataCompletion {
IRGenFunction &IGF;
LocalTypeDataCache &cache;
CanType rootTy;
GraphNodeWorklist<CanType, 4> worklist;

public:
TransitiveMetadataCompletion(IRGenFunction &IGF, LocalTypeDataCache &cache,
CanType rootTy)
: IGF(IGF), cache(cache), rootTy(rootTy) {}

void complete();

private:
/// Marks the metadata record currently locally cached corresponding to \p ty
/// complete.
///
/// Returns whether \p ty's transitive metadata should be marked complete.
bool visit(CanType ty) {
// If it's the root type, it's already been marked complete, but we want to
// mark its transitively dependent metadata as complete.
if (ty == rootTy)
return true;
auto key = LocalTypeDataKey(ty, LocalTypeDataKind::forFormalTypeMetadata());
// The metadata record was already marked complete. When that was done, the
// records for types it has transitive completeness requirements on would
// have been marked complete, if they had already been materialized.
//
// Such records may have been materialized since then in an abstract state,
// but that is an unlikely case and scanning again would incur compile-time
// overhead.
if (cache.advanceStateInScope(IGF, key, MetadataState::Complete) ==
LocalTypeDataCache::StateAdvancement::AlreadyAtLeast)
return false;
return true;
}
};

void TransitiveMetadataCompletion::complete() {
worklist.initialize(rootTy);

while (auto ty = worklist.pop()) {
if (!visit(ty)) {
// The transitively dependent metadata of `ty` doesn't need to be marked
// complete.
continue;
}

// Walk into every type that `ty` has transitive completeness requirements
// on and mark each one transitively complete.
//
// This should mirror findAnyTransitiveMetadata: every type whose metadata
// is visited (i.e. has predicate called on it) by that function should be
// pushed onto the worklist.
if (auto ct = dyn_cast<ClassType>(ty)) {
if (auto rawSuperTy = ct->getSuperclass()) {
auto superTy = rawSuperTy->getCanonicalType();
worklist.insert(superTy);
}
} else if (auto bgt = dyn_cast<BoundGenericType>(ty)) {
if (auto ct = dyn_cast<BoundGenericClassType>(bgt)) {
if (auto rawSuperTy = ct->getSuperclass()) {
auto superTy = rawSuperTy->getCanonicalType();
worklist.insert(superTy);
}
}
for (auto arg : bgt->getExpandedGenericArgs()) {
auto childTy = arg->getCanonicalType();
worklist.insert(childTy);
}
} else if (auto tt = dyn_cast<TupleType>(ty)) {
for (auto elt : tt.getElementTypes()) {
worklist.insert(elt);
}
}
}
}

} // end anonymous namespace

void IRGenFunction::setScopedLocalTypeMetadata(CanType rootTy,
MetadataResponse response) {
setScopedLocalTypeMetadataImpl(*this, rootTy, response);

if (response.getStaticLowerBoundOnState() != MetadataState::Complete)
return;

// If the metadata record is complete, then it is _transitively_ complete.
// So every metadata record that it has transitive completeness requirements
// on must also be complete.
//
// Mark all such already materialized metadata that the given type has
// transitive completeness requirements on as complete.
TransitiveMetadataCompletion(*this, getOrCreateLocalTypeData(), rootTy)
.complete();
}

void IRGenFunction::setScopedLocalTypeData(CanType type,
Expand All @@ -404,8 +585,10 @@ void IRGenFunction::setScopedLocalTypeDataForLayout(SILType type,
}

void IRGenFunction::setScopedLocalTypeData(LocalTypeDataKey key,
MetadataResponse value) {
maybeEmitDebugInfoForLocalTypeData(*this, key, value);
MetadataResponse value,
bool mayEmitDebugInfo) {
if (mayEmitDebugInfo)
maybeEmitDebugInfoForLocalTypeData(*this, key, value);

// Register with the active ConditionalDominanceScope if necessary.
bool isConditional = isConditionalDominancePoint();
Expand Down
16 changes: 16 additions & 0 deletions lib/IRGen/LocalTypeData.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,22 @@ class LocalTypeDataCache {
MetadataResponse tryGet(IRGenFunction &IGF, LocalTypeDataKey key,
bool allowAbstract, DynamicMetadataRequest request);

/// Whether the cached state was advanced or otherwise why not.
enum class StateAdvancement {
/// No entry whose state could be advanced was found.
NoEntry,
/// An entry was found whose state was already advanced at least as far as
/// the indicated state.
AlreadyAtLeast,
/// The state was advanced.
Advanced,
};

/// Advances the state cached for \p key to \p state within the active
/// dominance scope.
StateAdvancement advanceStateInScope(IRGenFunction &IGF, LocalTypeDataKey key,
MetadataState state);

/// Add a new concrete entry to the cache at the given definition point.
void addConcrete(DominancePoint point, bool isConditional,
LocalTypeDataKey key, MetadataResponse value) {
Expand Down
Loading