You don’t need a second open_existential_ref in the _wrap_inc<T: SumProtocol>
function. It should look something like this:
sil @_wrap_inc : $@convention(thin) <T where T : SumProtocol> (@owned T, Int)
-> Int {
bb0(%0 : $T, %1 : $Int):
%5 = witness_method $T, #SumProtocol.inc!1 : <Self where Self : SumProtocol>
(Self) -> (Int) -> Int : $@convention(witness_method: SumProtocol) <τ_0_0 where
τ_0_0 : SumProtocol> (Int, @guaranteed τ_0_0) -> Int
%6 = apply %5<T>(%1, %0) : $@convention(witness_method: SumProtocol) <τ_0_0
where τ_0_0 : SumProtocol> (Int, @guaranteed τ_0_0) -> Int
destroy_value %0 : $T
return %6 : $Int
}
In the other function it looks like you need to apply the proper substitution
list to the apply instruction:
sil hidden [thunk] [always_inline]
@_T04main8wrap_incSiAA11SumProtocol_p1a_Si3valtF : $@convention(thin) (@owned
SumProtocol, Int) -> Int {
bb0(%0 : $SumProtocol, %1 : $Int):
// function_ref specialized wrap_inc(a:val:)
%2 = function_ref @_T04main8wrap_incSiAA11SumProtocol_p1a_Si3valtFTf4nn_n
%3 = open_existential_ref %0 : $SumProtocol to
$@opened("E6196082-DF72-11E7-8C84-420039484801") SumProtocol
%4 = apply %2<τ_0_0>(%3, %1) : $@convention(thin) <τ_0_0 where τ_0_0 :
SumProtocol> (@owned τ_0_0, Int) -> Int // user: %5
τ_0_0 should have been substituted by the opened type:
$@opened("E6196082-DF72-11E7-8C84-420039484801”) SumProtocol.
%3 = open_existential_ref %0 : $SumProtocol to
$@opened("E6196082-DF72-11E7-8C84-420039484801") SumProtocol
%4 = apply %2<@opened("E6196082-DF72-11E7-8C84-420039484801”)
SumProtocol>(%3, %1) : $@convention(thin) <τ_0_0 where τ_0_0 : SumProtocol>
(@owned τ_0_0, Int) -> Int
Probably, you have to pass the right SubstitutionList to the createApplyInst
call.
The peephole that propagates types from an init existential Slava referred to
is here:
https://github.com/apple/swift/blob/master/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp#L974
(SILCombiner::propagateConcreteTypeOfInitExistential)
Here is a test case that shows how the type from the init existential is
propagated (instead of a generic type ’T’ as in the test case, in your case it
would be the class type SumClass):
https://github.com/apple/swift/blob/master/test/SILOptimizer/sil_combine.sil#L2569
> On Dec 13, 2017, at 11:39 AM, Raj Barik via swift-dev <[email protected]>
> wrote:
>
> Slava,
>
> I have two (clarification) questions in your proposed implementation:
>
> Original Function:
> @inline(never) internal func wrap_inc(a:SumProtocol, val:Int) -> Int{
> return a.increment(i:val)
> }
> Transformed code:
> @inline(always) internal func wrap_inc(a: SumProtocol, val: Int) -> Int {
> // opening an existential cannot be expressed in Swift, but it can in SIL…
> let _a = a open as T
>
> return _wrap_inc(_a, val)
> }
>
> @inline(never) internal func _wrap_inc<T : SumProtocol>(_a:T, val:Int) -> Int{
> return _a.increment(i:val)
> }
> ****************************************************************************************
> In the above code sequence, did you mean that "let _a = a open as T" opens
> "a:SumProtocol" using open_existential_ref instruction as "SumClass" which
> is the concrete type of a or it is opened as the "$@opened SumProtocol". In
> both cases, the open_existential_ref in the original function is still there
> and giving rise to opening the existential twice. Did you also intended that
> the _wrap_inc function is rewritten to eliminate the open_existential_ref as
> well (this is more complicated if the protocol is passed down a call chain)?
> So, I do not really understand what the "let _a = a open as T" is suggesting.
> The other part of the confusion is about the peephole optimization which
> optimizes the code sequence consisting of the creation of object for SumClass
> and then the init_existential_ref and followed by the open_existential_ref.
> Can you clarify?
>
> Thanks.
>
>
> On Wed, Nov 29, 2017 at 1:43 PM, Slava Pestov <[email protected]> wrote:
> Hi Raj,
>
> The way I would approach this problem is first, turn a function taking a
> protocol value into one taking a protocol-constrained generic parameter. So
>
> @inline(never) internal func wrap_inc(a:SumProtocol, val:Int) -> Int{
> return a.increment(i:val)
> }
>
> Would become
>
> @inline(always) internal func wrap_inc(a: SumProtocol, val: Int) -> Int {
> // opening an existential cannot be expressed in Swift, but it can in SIL…
> let _a = a open as T
>
> return _wrap_inc(_a, val)
> }
>
> @inline(never) internal func _wrap_inc<T : SumProtocol>(_a:T, val:Int) -> Int{
> let a: SomeProtocol = _a
> return a.increment(i:val)
> }
>
> (Note that the existing function signature specialization pass performs a
> similar transformation where it creates a new function with the same body as
> the old function but a different signature, and replaces the old function
> with a short thunk that transforms arguments and results and calls the new
> function.)
>
> At this point, the existing “initialize existential with concrete type”
> peephole in the SILCombiner should eliminate the existential (but the
> peephole doesn’t work in 100% of cases yet):
>
> @inline(always) internal func wrap_inc(a: SumProtocol, val: Int) -> Int {
> // opening an existential cannot be expressed in Swift, but it can in SIL…
> let _a = a open as T
>
> return _wrap_inc(_a, val)
> }
>
> @inline(never) internal func _wrap_inc<T : SumProtocol>(_a:T, val:Int) -> Int{
> return _a.increment(i:val)
> }
>
> Now, if I have a call to wrap_inc somewhere,
>
> internal let magic:SumProtocol = SumClass(base:10)
> _ = wrap_inc(magic)
>
> Then the optimizer will inline the thunk, giving you a call to _wrap_inc. The
> existential value built from the SumClass instance is immediately opened so
> it will be peepholed away. At this point you have a call of a generic
> function _wrap_inc with a concrete type SumClass, and the generic specializer
> can produce a specialization of it.
>
> Notice how this approach combines several existing optimizations and only
> requires adding a relatively simple new transformation, and possibly
> improving some of the existing optimizations to cover more cases.
>
> Slava
>
>> On Nov 29, 2017, at 11:30 AM, Raj Barik via swift-dev <[email protected]>
>> wrote:
>>
>> Hi,
>>
>> I am thinking about writing a Protocol Devirtualizer Pass that specializes
>> functions that take Protocols as arguments to transform them with concrete
>> types instead of protocol types when the concrete types can be determined
>> statically by some compiler analysis. This is the first step of the
>> transformation that I am proposing. My goal is to extend this to eliminate
>> the original function implementation and also to remove the corresponding
>> protocol type (by deleting it from the witness table), if possible. For
>> simple cases, where the protocol is only used for mocking for example and
>> that there is just one class that conforms to it, we should be able to
>> eliminate the protocol altogether. This is the second and final step of the
>> transformation. Does anyone see any issues with both these steps? Arnold
>> from Apple pointed out that there might be demangling issues when the
>> protocol is eliminated. Any ideas on how to fix the demangling issues?
>> Moreover, would such a pass be helpful to Swift folks?
>>
>> Original code:
>>
>>
>> protocol SumProtocol: class {
>> func increment(i:Int) -> Int
>> }
>>
>> internal class SumClass: SumProtocol {
>> var a:Int
>> init(base:Int) {
>> self.a = base
>> }
>> func increment(i:Int) -> Int {
>> self.a += i
>> return self.a
>> }
>> }
>>
>> @inline(never) internal func wrap_inc(a:SumProtocol, val:Int) -> Int{
>> return a.increment(i:val)
>> }
>>
>> internal let magic:SumProtocol = SumClass(base:10)
>> print("c=\(wrap_inc(a:magic,val:10))")
>>
>>
>> After Step 1:
>>
>>
>> protocol SumProtocol: class {
>> func increment(i:Int) -> Int
>> }
>>
>> internal class SumClass: SumProtocol {
>> var a:Int
>> init(base:Int) {
>> self.a = base
>> }
>> func increment(i:Int) -> Int {
>> self.a += i
>> return self.a
>> }
>> }
>>
>> @inline(never) internal func wrap_inc(a:SumProtocol, val:Int) -> Int{
>> return a.increment(i:val)
>> }
>>
>> @inline(never) internal func wrap_inc_1(a:SumClass, val:Int) -> Int{
>> return a.increment(i:val)
>> }
>>
>> internal let magic:SumClass = SumClass(base:10)
>> print("c=\(wrap_inc_1(a:magic,val:10))")
>>
>>
>> After Step 2:
>>
>> internal class SumClass {
>> var a:Int
>> init(base:Int) {
>> self.a = base
>> }
>> func increment(i:Int) -> Int {
>> self.a += i
>> return self.a
>> }
>> }
>>
>> @inline(never) internal func wrap_inc(a:SumClass, val:Int) -> Int{
>> return a.increment(i:val)
>> }
>>
>> internal let magic:SumClass = SumClass(base:10)
>> print("c=\(wrap_inc(a:magic,val:10))")
>>
>> Any comments/thought on this transformation?
>>
>> Best,
>> Raj
>> _______________________________________________
>> swift-dev mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-dev
>
>
> _______________________________________________
> swift-dev mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-dev
_______________________________________________
swift-dev mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-dev