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 <swift-dev@swift.org> > 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 <spes...@apple.com> 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 <swift-dev@swift.org> >> 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 >> swift-dev@swift.org >> https://lists.swift.org/mailman/listinfo/swift-dev > > > _______________________________________________ > swift-dev mailing list > swift-dev@swift.org > https://lists.swift.org/mailman/listinfo/swift-dev _______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev