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,
|
||||
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.
|
||||
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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();
|
||||
|
||||
@@ -271,6 +271,22 @@ public:
|
||||
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) {
|
||||
|
||||
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