Sure, I can :) On Thu, Dec 21, 2017 at 12:24 PM, Michael Gottesman <mgottes...@apple.com> wrote:
> Is it possible to merge them? > > > On Dec 21, 2017, at 11:07 AM, Raj Barik via swift-dev < > swift-dev@swift.org> wrote: > > > > Hi, > > > > Thanks. > > > > > > Are you implementing it as a separate pass, or is it part of function > signature specialization? > > > > > > I am currently implementing this as a separate pass. There is some code > overlap between the two (FunctionSignatureOpt and ProtocolDevirtualizerOpt) > in terms of checking which functions can be optimized. Barring that, the > code is quite different even though they follow the same pattern (thunk and > a separate function). > > > > --Raj > > > > > > > > Slava > > > >> > >> @inline(never) internal func wrap_inc_optional(a:SumProtocol?, > val:Int) -> Int?{ > >> return a?.increment(i:val) > >> } > >> > >> The generated SIL looks something like this: > >> > >> sil hidden [noinline] > >> @_T04main21wrap_inc_optionalSiSgAA11SumProtocol_pSg1a_Si3valtF > : $@convention(thin) (@owned Optional<SumProtocol>, Int) -> Optional<Int> { > >> // %0 // users: %11, %4, > %7, %2 > >> // %1 // users: %10, %3 > >> bb0(%0 : $Optional<SumProtocol>, %1 : $Int): > >> debug_value %0 : $Optional<SumProtocol>, let, name "a", argno 1 // > id: %2 > >> debug_value %1 : $Int, let, name "val", argno 2 // id: %3 > >> switch_enum %0 : $Optional<SumProtocol>, case > #Optional.some!enumelt.1: bb2, case #Optional.none!enumelt: bb1 // id: %4 > >> > >> bb1: // Preds: bb0 > >> %5 = enum $Optional<Int>, #Optional.none!enumelt // user: %6 > >> br bb3(%5 : $Optional<Int>) // id: %6 > >> > >> bb2: // Preds: bb0 > >> %7 = unchecked_enum_data %0 : $Optional<SumProtocol>, > #Optional.some!enumelt.1 // user: %8 > >> %8 = open_existential_ref %7 : $SumProtocol to > $@opened("F0395A0A-E5DE-11E7-A06A-420039484801") SumProtocol // users: > %10, %10, %9 > >> %9 = witness_method $@opened("F0395A0A-E5DE-11E7-A06A-420039484801") > SumProtocol, #SumProtocol.increment!1 : <Self where Self : SumProtocol> > (Self) -> (Int) -> Int, %8 : $@opened("F0395A0A-E5DE-11E7-A06A-420039484801") > SumProtocol : $@convention(witness_method) <τ_0_0 where τ_0_0 : > SumProtocol> (Int, @guaranteed τ_0_0) -> Int // type-defs: %8; user: %10 > >> %10 = apply %9<@opened("F0395A0A-E5DE-11E7-A06A-420039484801") > SumProtocol>(%1, %8) : $@convention(witness_method) <τ_0_0 where τ_0_0 : > SumProtocol> (Int, @guaranteed τ_0_0) -> Int // type-defs: %8; user: %12 > >> release_value %0 : $Optional<SumProtocol> // id: %11 > >> %12 = enum $Optional<Int>, #Optional.some!enumelt.1, %10 : $Int // > user: %13 > >> br bb3(%12 : $Optional<Int>) // id: %13 > >> > >> // %14 // user: %15 > >> bb3(%14 : $Optional<Int>): // Preds: bb1 bb2 > >> return %14 : $Optional<Int> // id: %15 > >> > >> > >> The above branching code (in red) in the SIL makes it non-trivial to > abstract out the non-nil path to a generic outlined method while keeping > the branching code in the thunk and also its not clear if the SILCombiner > peephole optimizer will actually come into affect for this scenario > (because of the branching code getting inlined in the caller). It also > gets more complicated if there are more than one optional types as > parameter to wrap_inc_optional. Any clue on how one can handle optional > types for devirtualization or if there are any existing transformations in > Swift compiler that can help implement this easily? Thanks. > >> > >> -R > >> > >> > >> > >> On Wed, Dec 13, 2017 at 3:28 PM, Arnold Schwaighofer < > aschwaigho...@apple.com> wrote: > >> 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 > >
_______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev