mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[IRGen] Promote generic args to complete.
When metadata record for a generic type is locally cached as ::Complete, the metadata is known to be complete in that scope. If it's for a generic type, being complete means that all of its recursive generic arguments are also complete. Previously, though, that fact was not exploited. So a metadata record for such an argument which was originally obtained via an incomplete request would remain cached in that incomplete state even when a generic type in which it appeared as a generic argument was cached as complete. At worst, the result was a runtime call (checkMetadataState) to promote the complete the metadata record for the recursive argument. Here, when a bound generic type's metadata is locally cached as complete, its recursive generic arguments are visited; for each such type for which a metadata record is already locally cached, the preexisting record is recached as complete.
This commit is contained in:
@@ -673,7 +673,8 @@ public:
|
|||||||
MetadataResponse tryGetConcreteLocalTypeData(LocalTypeDataKey key,
|
MetadataResponse tryGetConcreteLocalTypeData(LocalTypeDataKey key,
|
||||||
DynamicMetadataRequest request);
|
DynamicMetadataRequest request);
|
||||||
void setUnscopedLocalTypeData(LocalTypeDataKey key, MetadataResponse value);
|
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
|
/// Given a concrete type metadata node, add all the local type data
|
||||||
/// that we can reach from it.
|
/// that we can reach from it.
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "swift/AST/IRGenOptions.h"
|
#include "swift/AST/IRGenOptions.h"
|
||||||
#include "swift/AST/PackConformance.h"
|
#include "swift/AST/PackConformance.h"
|
||||||
#include "swift/AST/ProtocolConformance.h"
|
#include "swift/AST/ProtocolConformance.h"
|
||||||
|
#include "swift/Basic/GraphNodeWorklist.h"
|
||||||
#include "swift/SIL/SILModule.h"
|
#include "swift/SIL/SILModule.h"
|
||||||
|
|
||||||
using namespace swift;
|
using namespace swift;
|
||||||
@@ -304,6 +305,84 @@ LocalTypeDataCache::tryGet(IRGenFunction &IGF, LocalTypeDataKey key,
|
|||||||
llvm_unreachable("bad cache entry kind");
|
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
|
MetadataResponse
|
||||||
LocalTypeDataCache::AbstractCacheEntry::follow(IRGenFunction &IGF,
|
LocalTypeDataCache::AbstractCacheEntry::follow(IRGenFunction &IGF,
|
||||||
AbstractSource &source,
|
AbstractSource &source,
|
||||||
@@ -381,10 +460,112 @@ IRGenFunction::setScopedLocalTypeMetadataForLayout(SILType type,
|
|||||||
setScopedLocalTypeData(key, response);
|
setScopedLocalTypeData(key, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGenFunction::setScopedLocalTypeMetadata(CanType type,
|
namespace {
|
||||||
MetadataResponse response) {
|
|
||||||
|
void setScopedLocalTypeMetadataImpl(IRGenFunction &IGF, CanType type,
|
||||||
|
MetadataResponse response) {
|
||||||
auto key = LocalTypeDataKey(type, LocalTypeDataKind::forFormalTypeMetadata());
|
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,
|
void IRGenFunction::setScopedLocalTypeData(CanType type,
|
||||||
@@ -404,8 +585,10 @@ void IRGenFunction::setScopedLocalTypeDataForLayout(SILType type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IRGenFunction::setScopedLocalTypeData(LocalTypeDataKey key,
|
void IRGenFunction::setScopedLocalTypeData(LocalTypeDataKey key,
|
||||||
MetadataResponse value) {
|
MetadataResponse value,
|
||||||
maybeEmitDebugInfoForLocalTypeData(*this, key, value);
|
bool mayEmitDebugInfo) {
|
||||||
|
if (mayEmitDebugInfo)
|
||||||
|
maybeEmitDebugInfoForLocalTypeData(*this, key, value);
|
||||||
|
|
||||||
// Register with the active ConditionalDominanceScope if necessary.
|
// Register with the active ConditionalDominanceScope if necessary.
|
||||||
bool isConditional = isConditionalDominancePoint();
|
bool isConditional = isConditionalDominancePoint();
|
||||||
|
|||||||
@@ -271,6 +271,22 @@ public:
|
|||||||
MetadataResponse tryGet(IRGenFunction &IGF, LocalTypeDataKey key,
|
MetadataResponse tryGet(IRGenFunction &IGF, LocalTypeDataKey key,
|
||||||
bool allowAbstract, DynamicMetadataRequest request);
|
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.
|
/// Add a new concrete entry to the cache at the given definition point.
|
||||||
void addConcrete(DominancePoint point, bool isConditional,
|
void addConcrete(DominancePoint point, bool isConditional,
|
||||||
LocalTypeDataKey key, MetadataResponse value) {
|
LocalTypeDataKey key, MetadataResponse value) {
|
||||||
|
|||||||
306
test/IRGen/minimal_metadata.sil
Normal file
306
test/IRGen/minimal_metadata.sil
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s
|
||||||
|
|
||||||
|
sil_stage canonical
|
||||||
|
|
||||||
|
import Builtin
|
||||||
|
|
||||||
|
protocol P {
|
||||||
|
associatedtype Assoc
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Box<T> {
|
||||||
|
var t: T
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @getBox : $<T> (@thick T.Type) -> (@out Box<T>)
|
||||||
|
sil @getBoxBox : $<T> (@thick T.Type) -> (@out Box<Box<T>>)
|
||||||
|
|
||||||
|
enum Affirm<T> {
|
||||||
|
case yes(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @getAffirm : $<T> (@thick T.Type) -> (@out Affirm<T>)
|
||||||
|
sil @getAffirmAffirm : $<T> (@thick T.Type) -> (@out Affirm<Affirm<T>>)
|
||||||
|
|
||||||
|
class K<T> {
|
||||||
|
var t: T
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @getK : $<T> (@thick T.Type) -> (@out K<T>)
|
||||||
|
sil @getKK : $<T> (@thick T.Type) -> (@out K<K<T>>)
|
||||||
|
|
||||||
|
sil @getTuple : $<T> (@thick T.Type) -> (@out (T, Builtin.Int1))
|
||||||
|
sil @getTupleTuple : $<T> (@thick T.Type) -> (@out ((T, Builtin.Int1), Builtin.Int1))
|
||||||
|
|
||||||
|
struct Vari<each T> {
|
||||||
|
var ts : (repeat each T)
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @getVari : $<each T> () -> (@out Vari<each T>)
|
||||||
|
|
||||||
|
sil @takeTy : $<T> (@thick T.Type) -> ()
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_struct(ptr %T, ptr %T.P)
|
||||||
|
// // The metadata for T.Assoc is for obtained in state ::Abstract VVVVVVV
|
||||||
|
// %0 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(i64 255, ptr %T.P, ptr %T, ...
|
||||||
|
// %T.Assoc = extractvalue %swift.metadata_response %0, 0
|
||||||
|
// // It's used to obtain the metadata for Box<T.Assoc> in state ::Complete
|
||||||
|
// %2 = call swiftcc %swift.metadata_response @"$s4main3BoxVMa"(i64 0, ptr %T.Assoc)
|
||||||
|
// // Now that we have the complete metadata for Box<T.Assoc>, the metadata for T.Assoc is also complete.
|
||||||
|
//
|
||||||
|
// // Verify that the metadata for T.Assoc isn't spuriously completed again.
|
||||||
|
// CHECK-NOT: call swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, ptr %T.Assoc)
|
||||||
|
// // Verify that the metadata for T.Assoc from swift_getAssociatedTypeWitness is reused directly in the getBox call.
|
||||||
|
// CHECK: call swiftcc void @getBox(ptr noalias sret(%swift.opaque) %6, ptr %T.Assoc, ptr %T.Assoc)
|
||||||
|
// }
|
||||||
|
sil @test_struct : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%box = alloc_stack $Box<T.Assoc>
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%getBox = function_ref @getBox : $@convention(thin) <T> (@thick T.Type) -> (@out Box<T>)
|
||||||
|
apply %getBox<T.Assoc>(%box, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out Box<T>)
|
||||||
|
destroy_addr %box : $*Box<T.Assoc>
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %box : $*Box<T.Assoc>
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_struct_nested(ptr %T, ptr %T.P)
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getBox
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getBoxBox
|
||||||
|
// }
|
||||||
|
sil @test_struct_nested : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%box = alloc_stack $Box<Box<T.Assoc>>
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mtb = metatype $@thick Box<T.Assoc>.Type
|
||||||
|
%getBox = function_ref @getBox : $@convention(thin) <T> (@thick T.Type) -> (@out Box<T>)
|
||||||
|
apply %getBox<Box<T.Assoc>>(%box, %mtb) : $@convention(thin) <T> (@thick T.Type) -> (@out Box<T>)
|
||||||
|
destroy_addr %box : $*Box<Box<T.Assoc>>
|
||||||
|
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%getBoxBox = function_ref @getBoxBox : $@convention(thin) <T> (@thick T.Type) -> (@out Box<Box<T>>)
|
||||||
|
apply %getBoxBox<T.Assoc>(%box, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out Box<Box<T>>)
|
||||||
|
destroy_addr %box : $*Box<Box<T.Assoc>>
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %box : $*Box<Box<T.Assoc>>
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_enum(ptr %T, ptr %T.P)
|
||||||
|
// CHECK-NOT: call swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, ptr %T.Assoc)
|
||||||
|
// CHECK: call swiftcc void @getAffirm
|
||||||
|
// }
|
||||||
|
sil @test_enum : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%affirm = alloc_stack $Affirm<T.Assoc>
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%getAffirm = function_ref @getAffirm : $@convention(thin) <T> (@thick T.Type) -> (@out Affirm<T>)
|
||||||
|
apply %getAffirm<T.Assoc>(%affirm, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out Affirm<T>)
|
||||||
|
destroy_addr %affirm : $*Affirm<T.Assoc>
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %affirm : $*Affirm<T.Assoc>
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_enum_nested(ptr %T, ptr %T.P)
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getAffirm
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getAffirmAffirm
|
||||||
|
// }
|
||||||
|
sil @test_enum_nested : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%affirm = alloc_stack $Affirm<Affirm<T.Assoc>>
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mtb = metatype $@thick Affirm<T.Assoc>.Type
|
||||||
|
%getAffirm = function_ref @getAffirm : $@convention(thin) <T> (@thick T.Type) -> (@out Affirm<T>)
|
||||||
|
apply %getAffirm<Affirm<T.Assoc>>(%affirm, %mtb) : $@convention(thin) <T> (@thick T.Type) -> (@out Affirm<T>)
|
||||||
|
destroy_addr %affirm : $*Affirm<Affirm<T.Assoc>>
|
||||||
|
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%getAffirmAffirm = function_ref @getAffirmAffirm : $@convention(thin) <T> (@thick T.Type) -> (@out Affirm<Affirm<T>>)
|
||||||
|
apply %getAffirmAffirm<T.Assoc>(%affirm, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out Affirm<Affirm<T>>)
|
||||||
|
destroy_addr %affirm : $*Affirm<Affirm<T.Assoc>>
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %affirm : $*Affirm<Affirm<T.Assoc>>
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_class(ptr %T, ptr %T.P)
|
||||||
|
// CHECK-NOT: call swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, ptr %T.Assoc)
|
||||||
|
// CHECK: call swiftcc void @getK
|
||||||
|
// }
|
||||||
|
sil @test_class : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%k = alloc_stack $K<T.Assoc>
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%getK = function_ref @getK : $@convention(thin) <T> (@thick T.Type) -> (@out K<T>)
|
||||||
|
apply %getK<T.Assoc>(%k, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out K<T>)
|
||||||
|
destroy_addr %k : $*K<T.Assoc>
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %k : $*K<T.Assoc>
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_class_nested(ptr %T, ptr %T.P)
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getK
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getKK
|
||||||
|
// }
|
||||||
|
sil @test_class_nested : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%k = alloc_stack $K<K<T.Assoc>>
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mtb = metatype $@thick K<T.Assoc>.Type
|
||||||
|
%getK = function_ref @getK : $@convention(thin) <T> (@thick T.Type) -> (@out K<T>)
|
||||||
|
apply %getK<K<T.Assoc>>(%k, %mtb) : $@convention(thin) <T> (@thick T.Type) -> (@out K<T>)
|
||||||
|
destroy_addr %k : $*K<K<T.Assoc>>
|
||||||
|
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%getKK = function_ref @getKK : $@convention(thin) <T> (@thick T.Type) -> (@out K<K<T>>)
|
||||||
|
apply %getKK<T.Assoc>(%k, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out K<K<T>>)
|
||||||
|
destroy_addr %k : $*K<K<T.Assoc>>
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %k : $*K<K<T.Assoc>>
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_tuple(ptr %T, ptr %T.P)
|
||||||
|
// CHECK-NOT: call swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, ptr %T.Assoc)
|
||||||
|
// CHECK: call swiftcc void @getTuple
|
||||||
|
// }
|
||||||
|
sil @test_tuple : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%tuple = alloc_stack $(T.Assoc, Builtin.Int1)
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%get = function_ref @getTuple : $@convention(thin) <T> (@thick T.Type) -> (@out (T, Builtin.Int1))
|
||||||
|
apply %get<T.Assoc>(%tuple, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out (T, Builtin.Int1))
|
||||||
|
destroy_addr %tuple : $*(T.Assoc, Builtin.Int1)
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %tuple : $*(T.Assoc, Builtin.Int1)
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_tuple_nested(ptr %T, ptr %T.P)
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getTuple
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @getTupleTuple
|
||||||
|
// }
|
||||||
|
sil @test_tuple_nested : $@convention(thin) <T : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%tuple = alloc_stack $((T.Assoc, Builtin.Int1), Builtin.Int1)
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mtb = metatype $@thick (T.Assoc, Builtin.Int1).Type
|
||||||
|
%get = function_ref @getTuple : $@convention(thin) <T> (@thick T.Type) -> (@out (T, Builtin.Int1))
|
||||||
|
apply %get<(T.Assoc, Builtin.Int1)>(%tuple, %mtb) : $@convention(thin) <T> (@thick T.Type) -> (@out (T, Builtin.Int1))
|
||||||
|
destroy_addr %tuple : $*((T.Assoc, Builtin.Int1), Builtin.Int1)
|
||||||
|
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%getTupleTuple = function_ref @getTupleTuple : $@convention(thin) <T> (@thick T.Type) -> (@out ((T, Builtin.Int1), Builtin.Int1))
|
||||||
|
apply %getTupleTuple<T.Assoc>(%tuple, %mt) : $@convention(thin) <T> (@thick T.Type) -> (@out ((T, Builtin.Int1), Builtin.Int1))
|
||||||
|
destroy_addr %tuple : $*((T.Assoc, Builtin.Int1), Builtin.Int1)
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %tuple : $*((T.Assoc, Builtin.Int1), Builtin.Int1)
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@test_variadic_struct(ptr %T, ptr %U, ptr %T.P, ptr %U.P)
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @takeTy
|
||||||
|
// CHECK-NOT: @swift_checkMetadataState
|
||||||
|
// CHECK: call swiftcc void @takeTy
|
||||||
|
// }
|
||||||
|
sil @test_variadic_struct : $@convention(thin) <T : P, U : P> () -> () {
|
||||||
|
entry:
|
||||||
|
%vari = alloc_stack $Vari<T.Assoc, U.Assoc>
|
||||||
|
cond_br undef, left, right
|
||||||
|
|
||||||
|
left:
|
||||||
|
%mt = metatype $@thick T.Assoc.Type
|
||||||
|
%takeTy = function_ref @takeTy : $@convention(thin) <T> (@thick T.Type) -> ()
|
||||||
|
apply %takeTy<T.Assoc>(%mt) : $@convention(thin) <T> (@thick T.Type) -> ()
|
||||||
|
%mu = metatype $@thick U.Assoc.Type
|
||||||
|
apply %takeTy<U.Assoc>(%mu) : $@convention(thin) <T> (@thick T.Type) -> ()
|
||||||
|
br exit
|
||||||
|
|
||||||
|
right:
|
||||||
|
br exit
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dealloc_stack %vari : $*Vari<T.Assoc, U.Assoc>
|
||||||
|
%retval = tuple ()
|
||||||
|
return %retval : $()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user