Skip to content

Some fixes and clean-ups of the partial specialization implementation #9152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 1, 2017
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
15 changes: 14 additions & 1 deletion lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ static llvm::cl::opt<bool> SkipUnreachableMustBeLastErrors(

/// Returns true if A is an opened existential type or is equal to an
/// archetype from F's generic context.
static bool isArchetypeValidInFunction(ArchetypeType *A, SILFunction *F) {
static bool isArchetypeValidInFunction(ArchetypeType *A, const SILFunction *F) {
if (!A->getOpenedExistentialType().isNull())
return true;

Expand Down Expand Up @@ -825,6 +825,19 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
require(fnTy->isPolymorphic(),
"callee of apply with substitutions must be polymorphic");

// Each archetype occurring in the substitutions list should belong to the
// current function.
for (auto sub : subs) {
sub.getReplacement()->getCanonicalType().visit([&](CanType t) {
auto A = dyn_cast<ArchetypeType>(t);
if (!A)
return;
require(isArchetypeValidInFunction(A, &F),
"Replacment type of a substitution contains an ArchetypeType "
"that does not exist in the Caller's generic param list.");
});
}

// Apply the substitutions.
return fnTy->substGenericArgs(F.getModule(), subs);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/SILOptimizer/Utils/Generics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ ReabstractionInfo::ReabstractionInfo(ApplySite Apply, SILFunction *Callee,

SILFunction *Caller = nullptr;
if (Apply)
Caller = Apply.getCalleeFunction();
Caller = Apply.getFunction();

if (!EnablePartialSpecialization || !HasUnboundGenericParams) {
// Fast path for full specializations.
Expand Down
154 changes: 154 additions & 0 deletions test/SILOptimizer/partial_specialization.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -generic-specializer -sil-partial-specialization-with-generic-substitutions %s | %FileCheck %s

// Test different cases of partial specialization.
// In particular, test the correctness of partial specializaitons where substitutions may be generic.

sil_stage canonical

import Builtin
import Swift
import SwiftShims

@_silgen_name("use")
public func use<T>(_ t: T)

public protocol P {
}

public struct S<T> {
@inline(never) public init(_ t: T)
}

@inline(never) public func simple_generic_callee<U, V>(_ u: U, _ v: V)

public func simple_generic_caller1<U>(_ u: U)

public func simple_generic_caller2<U>(_ u: U)

// use
sil @use : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()

// S.init(_:)
sil [noinline] @_T022partial_specialization1SVACyxGxcfC : $@convention(method) <T> (@in T, @thin S<T>.Type) -> S<T>

// simple_generic_callee<A, B>(_:_:)
sil [noinline] @simple_generic_callee : $@convention(thin) <U, V> (@in U, @in V) -> () {
bb0(%0 : $*U, %1 : $*V):
// function_ref use
%4 = function_ref @use : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
%5 = alloc_stack $U
copy_addr %0 to [initialization] %5 : $*U
%7 = apply %4<U>(%5) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
dealloc_stack %5 : $*U
// function_ref use
%9 = function_ref @use : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
%10 = alloc_stack $V
copy_addr %1 to [initialization] %10 : $*V
%12 = apply %9<V>(%10) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
dealloc_stack %10 : $*V
destroy_addr %1 : $*V
destroy_addr %0 : $*U
%16 = tuple ()
return %16 : $()
} // end sil function 'simple_generic_callee'

// simple_generic_caller1<A>(_:)
sil @simple_generic_caller1 : $@convention(thin) <U> (@in U) -> () {
bb0(%0 : $*U):
// function_ref simple_generic_callee<A, B>(_:_:)
%2 = function_ref @simple_generic_callee : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
%3 = alloc_stack $U
copy_addr %0 to [initialization] %3 : $*U
%5 = integer_literal $Builtin.Int32, 1
%6 = struct $Int32 (%5 : $Builtin.Int32)
%7 = alloc_stack $Int32
store %6 to %7 : $*Int32
%9 = apply %2<U, Int32>(%3, %7) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
dealloc_stack %7 : $*Int32
dealloc_stack %3 : $*U
destroy_addr %0 : $*U
%13 = tuple ()
return %13 : $()
} // end sil function 'simple_generic_caller1'

// simple_generic_caller2<A>(_:)
sil @simple_generic_caller2 : $@convention(thin) <U> (@in U) -> () {
bb0(%0 : $*U):
// function_ref simple_generic_callee<A, B>(_:_:)
%2 = function_ref @simple_generic_callee : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
// function_ref S.init(_:)
%3 = function_ref @_T022partial_specialization1SVACyxGxcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin S<τ_0_0>.Type) -> S<τ_0_0>
%4 = metatype $@thin S<U>.Type
%5 = alloc_stack $U
copy_addr %0 to [initialization] %5 : $*U
%7 = apply %3<U>(%5, %4) : $@convention(method) <τ_0_0> (@in τ_0_0, @thin S<τ_0_0>.Type) -> S<τ_0_0>
dealloc_stack %5 : $*U
%9 = alloc_stack $S<U>
store %7 to %9 : $*S<U>
%11 = integer_literal $Builtin.Int32, 1
%12 = struct $Int32 (%11 : $Builtin.Int32)
%13 = alloc_stack $Int32
store %12 to %13 : $*Int32
%15 = apply %2<S<U>, Int32>(%9, %13) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
dealloc_stack %13 : $*Int32
dealloc_stack %9 : $*S<U>
destroy_addr %0 : $*U
%19 = tuple ()
return %19 : $()
} // end sil function 'simple_generic_caller2'

// Check that a partial specialization for simple_generic_callee<U, Int32> was created.
// CHECK-LABEL: sil shared [noinline] @_T021simple_generic_calleexs5Int32Vq_RszACRs0_r1_lItiy_Tp5 : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_0 == τ_0_1, τ_0_2 == Int32> (@in τ_0_0, Int32) -> ()


// Check that a partial specialization for simple_generic_callee<S<U>, Int32> was created.
// CHECK-LABEL: sil shared [noinline] @_T021simple_generic_callee4main1SVyxGs5Int32VAERs_AGRs0_r1_lItyy_Tp5 : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2 where τ_0_1 == S<τ_0_0>, τ_0_2 == Int32> (S<τ_0_0>, Int32) -> ()

// Check that no partial specializations are produced if all substitutions in the substitution list
// are archetypes.
// CHECK-LABEL: sil @simple_generic_caller3 : $@convention(thin) <U> (@in U) -> ()
// CHECK-NOT: specialized
// CHECK: function_ref @simple_generic_callee : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
// CHECK-NOT: specialized
// CHECK: // end sil function 'simple_generic_caller3'
// simple_generic_caller3<A>(_:)
sil @simple_generic_caller3 : $@convention(thin) <U> (@in U) -> () {
bb0(%0 : $*U):
// function_ref simple_generic_callee<A, B>(_:_:)
%2 = function_ref @simple_generic_callee : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
%3 = alloc_stack $U
copy_addr %0 to [initialization] %3 : $*U
%5 = apply %2<U, U>(%3, %0) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
dealloc_stack %3 : $*U
%7 = tuple ()
return %7 : $()
} // end sil function 'simple_generic_caller3'

// Check that partial specialization is not peformed for generic type parameters where
// the substitution contains an open existential.
// CHECK-LABEL: sil @simple_generic_caller4 : $@convention(thin) (@in P, Builtin.Int1) -> ()
// CHECK: function_ref @_T021simple_generic_calleexBi1_Bi1_Rs_r0_lItiy_Tp5 : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_1 == Builtin.Int1> (@in τ_0_0, Builtin.Int1) -> ()
// CHECK: // end sil function 'simple_generic_caller4'
sil @simple_generic_caller4 : $@convention(thin) (@in P, Builtin.Int1) -> () {
bb0(%0 : $*P, %1: $Builtin.Int1):
cond_br %1, bb1, bb2
bb1:
%2 = open_existential_addr mutable_access %0 : $*P to $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
%3 = alloc_stack $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
copy_addr [take] %2 to [initialization] %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
%5 = alloc_stack $Builtin.Int1
store %1 to %5 : $*Builtin.Int1
// function_ref simple_generic_callee<A, B>(_:_:)
%7 = function_ref @simple_generic_callee : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
%8 = apply %7<@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P, Builtin.Int1>(%3, %5) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
dealloc_stack %5 : $*Builtin.Int1
destroy_addr %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
dealloc_stack %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
br bb3
bb2:
destroy_addr %0 : $*P
br bb3
bb3:
%13 = tuple ()
return %13 : $()
} // end sil function 'simple_generic_caller4'