<div dir="ltr"><br><div>Thanks for the recommendations, Slava. Although I am able to create both the generic function and the wrapper thunk, I get a crash in the existing performance inliner pass while iterating over the apply instruction and trying to perform substitution. Here is the SIL that I generate:</div><div><br></div><div><div>sil hidden [thunk] [always_inline] @_T04main8wrap_incSiAA11SumProtocol_p1a_Si3valtF : $@convention(thin) (@owned SumProtocol, Int) -> Int {</div><div>// %0 // user: %3</div><div>// %1 // user: %4</div><div>bb0(%0 : $SumProtocol, %1 : $Int):</div><div> // function_ref specialized wrap_inc(a:val:)</div><div> %2 = function_ref @_T04main8wrap_incSiAA11SumProtocol_p1a_Si3valtFTf4nn_n : $@convention(thin) <τ_0_0 where τ_0_0 : SumProtocol> (@owned τ_0_0, Int) -> Int // user: %4</div><div> %3 = open_existential_ref %0 : $SumProtocol to $@opened("E6196082-DF72-11E7-8C84-420039484801") SumProtocol // user: %4</div><div> %4 = apply %2<τ_0_0>(%3, %1) : $@convention(thin) <τ_0_0 where τ_0_0 : SumProtocol> (@owned τ_0_0, Int) -> Int // user: %5</div><div> return %4 : $Int // id: %5</div><div>} // end sil function '_T04main8wrap_incSiAA11SumProtocol_p1a_Si3valtF'</div><div><br></div><div>// specialized wrap_inc(a:val:)</div><div>sil shared [noinline] @_T04main8wrap_incSiAA11SumProtocol_p1a_Si3valtFTf4nn_n : $@convention(thin) <τ_0_0 where τ_0_0 : SumProtocol> (@owned τ_0_0, Int) -> Int {</div><div>// %0 // users: %3, %2</div><div>// %1 // user: %4</div><div>bb0(%0 : $τ_0_0, %1 : $Int):</div><div> debug_value_addr %0 : $τ_0_0, let, name "a" // id: %2</div><div> %3 = unchecked_ref_cast %0 : $τ_0_0 to $SumProtocol // user: %4</div><div> br bb1(%3 : $SumProtocol, %1 : $Int) // id: %4</div><div><br></div><div>// %5 // users: %12, %9, %7</div><div>// %6 // users: %11, %8</div><div>bb1(%5 : $SumProtocol, %6 : $Int): // Preds: bb0</div><div> debug_value %5 : $SumProtocol, let, name "a", argno 1 // id: %7</div><div> debug_value %6 : $Int, let, name "val", argno 2 // id: %8</div><div> %9 = open_existential_ref %5 : $SumProtocol to $@opened("E60585BC-DF72-11E7-8C84-420039484801") SumProtocol // users: %11, %11, %10</div><div> %10 = witness_method $@opened("E60585BC-DF72-11E7-8C84-420039484801") SumProtocol, #SumProtocol.increment!1 : <Self where Self : SumProtocol> (Self) -> (Int) -> Int, %9 : $@opened("E60585BC-DF72-11E7-8C84-420039484801") SumProtocol : $@convention(witness_method) <τ_0_0 where τ_0_0 : SumProtocol> (Int, @guaranteed τ_0_0) -> Int // type-defs: %9; user: %11</div><div> %11 = apply %10<@opened("E60585BC-DF72-11E7-8C84-420039484801") SumProtocol>(%6, %9) : $@convention(witness_method) <τ_0_0 where τ_0_0 : SumProtocol> (Int, @guaranteed τ_0_0) -> Int // type-defs: %9; user: %13</div><div> strong_release %5 : $SumProtocol // id: %12</div><div> return %11 : $Int // id: %13</div><div>} // end sil function '_T04main8wrap_incSiAA11SumProtocol_p1a_Si3valtFTf4nn_n'</div></div><div><br></div><div><br></div><div><b>One more question</b>: Is it OK to open an existential reference twice? In the above code, I open the protocol in the thunk and also in the generic wrapper.</div><div><br></div><div><br></div><div><div>0 swift 0x000000010980e278 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40</div><div>1 swift 0x000000010980d1c6 llvm::sys::RunSignalHandlers() + 86</div><div>2 swift 0x000000010980e83e SignalHandler(int) + 366</div><div>3 libsystem_platform.dylib 0x00007fff7baa4f5a _sigtramp + 26</div><div>4 libdyld.dylib 0x00007fff7b824279 dyldGlobalLockRelease() + 0</div><div>5 libsystem_c.dylib 0x00007fff7b8d030a abort + 127</div><div>6 libsystem_c.dylib 0x00007fff7b898360 basename_r + 0</div><div>7 swift 0x00000001075230cb swift::SubstitutionMap::lookupSubstitution(swift::CanTypeWrapper<swift::SubstitutableType>) const + 651</div><div>8 swift 0x0000000107534904 llvm::Optional<swift::Type> llvm::function_ref<llvm::Optional<swift::Type> (swift::TypeBase*)>::callback_fn<substType(swift::Type, llvm::function_ref<swift::Type (swift::SubstitutableType*)>, llvm::function_ref<llvm::Optional<swift::ProtocolConformanceRef> (swift::CanType, swift::Type, swift::ProtocolType*)>, swift::SubstOptions)::$_18>(long, swift::TypeBase*) + 884</div><div>9 swift 0x0000000107531367 swift::Type::transformRec(llvm::function_ref<llvm::Optional<swift::Type> (swift::TypeBase*)>) const + 151</div><div>10 swift 0x000000010752fcb4 swift::Type::subst(llvm::function_ref<swift::Type (swift::SubstitutableType*)>, llvm::function_ref<llvm::Optional<swift::ProtocolConformanceRef> (swift::CanType, swift::Type, swift::ProtocolType*)>, swift::SubstOptions) const + 196</div><div>11 swift 0x0000000106fe9ad7 (anonymous namespace)::SILTypeSubstituter::visitType(swift::CanType) + 119</div><div>12 swift 0x0000000106fe953e swift::CanType swift::CanTypeVisitor<(anonymous namespace)::SILTypeSubstituter, swift::CanType>::visit<>(swift::CanType) + 94</div><div>13 swift 0x0000000106fe4e8f (anonymous namespace)::SILTypeSubstituter::substSILFunctionType(swift::CanTypeWrapper<swift::SILFunctionType>) + 607</div><div>14 swift 0x0000000106fe961f swift::CanType swift::CanTypeVisitor<(anonymous namespace)::SILTypeSubstituter, swift::CanType>::visit<>(swift::CanType) + 319</div><div>15 swift 0x0000000106fe49ea swift::SILType::subst(swift::SILModule&, llvm::function_ref<swift::Type (swift::SubstitutableType*)>, llvm::function_ref<llvm::Optional<swift::ProtocolConformanceRef> (swift::CanType, swift::Type, swift::ProtocolType*)>, swift::CanGenericSignature) const + 218</div><div>16 swift 0x0000000106fe4a63 swift::SILType::subst(swift::SILModule&, swift::SubstitutionMap const&) const + 51</div><div>17 swift 0x0000000106cc93ec swift::TypeSubstCloner<swift::SILInliner>::ApplySiteCloningHelper::ApplySiteCloningHelper(swift::ApplySite, swift::TypeSubstCloner<swift::SILInliner>&) + 220</div><div>18 swift 0x0000000106cbaff1 swift::TypeSubstCloner<swift::SILInliner>::visitApplyInst(swift::ApplyInst*) + 113</div><div>19 swift 0x0000000106caf993 swift::SILCloner<swift::SILInliner>::visitSILBasicBlock(swift::SILBasicBlock*) + 83</div><div>20 swift 0x0000000106caf3b8 swift::SILInliner::inlineFunction(swift::FullApplySite, llvm::ArrayRef<swift::SILValue>) + 1048</div><div>21 swift 0x0000000106dbd6e7 (anonymous namespace)::SILPerformanceInlinerPass::run() + 1735</div><div>22 swift 0x0000000106cd47cf swift::SILPassManager::runPassOnFunction(swift::SILFunctionTransform*, swift::SILFunction*) + 4015</div><div>23 swift 0x0000000106cd5947 swift::SILPassManager::runFunctionPasses(llvm::ArrayRef<swift::SILFunctionTransform*>) + 1079</div><div>24 swift 0x0000000106cd6d94 swift::SILPassManager::runOneIteration() + 964</div><div>25 swift 0x00000001065ada1b swift::SILPassManager::executePassPipelinePlan(swift::SILPassPipelinePlan const&) + 187</div><div>26 swift 0x0000000106cdf652 swift::runSILOptimizationPasses(swift::SILModule&) + 114</div><div>27 swift 0x000000010646deb2 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 13634</div><div>28 swift 0x0000000106469a6a swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 3530</div><div>29 swift 0x000000010642aa60 main + 3360</div><div>30 libdyld.dylib 0x00007fff7b824145 start + 1</div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Nov 29, 2017 at 1:43 PM, Slava Pestov <span dir="ltr"><<a href="mailto:spestov@apple.com" target="_blank">spestov@apple.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space">Hi Raj,<div><br></div><div>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</div><span class=""><div><br></div><div>@inline(never) internal func wrap_inc(a:SumProtocol, val:Int) -> Int{<br> return a.increment(i:val)<br>}</div><div><br></div></span><div>Would become</div><div><br></div><div>@inline(always) internal func wrap_inc(a: SumProtocol, val: Int) -> Int {</div><div> // opening an existential cannot be expressed in Swift, but it can in SIL…</div><div> let _a = a open as T</div><div><br></div><div> return _wrap_inc(_a, val)</div><div>}</div><div><br></div><div>@inline(never) internal func _wrap_inc<T : SumProtocol>(_a:T, val:Int) -> Int{</div><div> let a: SomeProtocol = _a<br> return a.increment(i:val)<br>}</div><div><div><br></div><div>(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.)</div><div><br></div><div>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):</div><div><br></div><div><div>@inline(always) internal func wrap_inc(a: SumProtocol, val: Int) -> Int {</div><div> // opening an existential cannot be expressed in Swift, but it can in SIL…</div><div> let _a = a open as T</div><div><br></div><div> return _wrap_inc(_a, val)</div><div>}</div><div><br></div></div><div><div>@inline(never) internal func _wrap_inc<T : SumProtocol>(_a:T, val:Int) -> Int{</div><div> return _a.increment(i:val)<br>}</div></div><div><br></div><div>Now, if I have a call to wrap_inc somewhere, </div><span class=""><div><br></div><div>internal let magic:SumProtocol = SumClass(base:10)</div></span><div>_ = wrap_inc(magic)</div><div><br></div><div>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.</div><div><br></div><div>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.</div><span class="HOEnZb"><font color="#888888"><div><br></div><div>Slava</div><div><br></div></font></span><div><blockquote type="cite"><span class=""><div>On Nov 29, 2017, at 11:30 AM, Raj Barik via swift-dev <<a href="mailto:swift-dev@swift.org" target="_blank">swift-dev@swift.org</a>> wrote:</div><br class="m_2641232502470950Apple-interchange-newline"></span><div><div><div class="h5"><div dir="ltr"><div style="font-size:12.8px"><div>Hi,</div><div><br></div><div>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?</div><div><br></div><div><div style="font-size:12.8px"><b>Original code:</b></div></div><div><b><br></b></div><div><br></div><div>protocol SumProtocol: class {</div><div> func increment(i:Int) -> Int</div><div>}</div><div><br></div><div>internal class SumClass: SumProtocol {</div><div> var a:Int</div><div> init(base:Int) {</div><div> self.a = base</div><div> }</div><div> func increment(i:Int) -> Int {</div><div> self.a += i</div><div> return self.a</div><div> }</div><div>}</div><div><br></div><div>@inline(never) internal func wrap_inc(a:SumProtocol, val:Int) -> Int{</div><div> return a.increment(i:val)</div><div>}</div><div><br></div><div>internal let magic:SumProtocol = SumClass(base:10)</div><div>print("c=\(wrap_inc(a:magic,va<wbr>l:10))")</div></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><b>After Step 1:</b></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><div>protocol SumProtocol: class {</div><div> func increment(i:Int) -> Int</div><div>}</div><div><br></div><div>internal class SumClass: SumProtocol {</div><div> var a:Int</div><div> init(base:Int) {</div><div> self.a = base</div><div> }</div><div> func increment(i:Int) -> Int {</div><div> self.a += i</div><div> return self.a</div><div> }</div><div>}</div><div><br></div><div><div style="font-size:12.8px">@inline(never) internal func wrap_inc(a:SumProtocol, val:Int) -> Int{</div><div style="font-size:12.8px"> return a.increment(i:val)</div><div style="font-size:12.8px">}</div></div><div><br></div><div>@inline(never) internal func wrap_inc_1(a:SumClass, val:Int) -> Int{</div><div> return a.increment(i:val)</div><div>}</div><div><br></div><div>internal let magic:SumClass = SumClass(base:10)</div><div>print("c=\(wrap_inc_1(a:magic,<wbr>val:10))")</div></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><div><b>After Step 2:</b></div><div><b><br></b></div><div><div>internal class SumClass {</div><div> var a:Int</div><div> init(base:Int) {</div><div> self.a = base</div><div> }</div><div> func increment(i:Int) -> Int {</div><div> self.a += i</div><div> return self.a</div><div> }</div><div>}</div><div><br></div><div>@inline(never) <span style="font-size:12.8px">internal </span><span style="font-size:12.8px">func wrap_inc(a:SumClass, val:Int) -> Int{</span></div><div> return a.increment(i:val)</div><div>}</div><div><br></div><div>internal let magic:SumClass = SumClass(base:10)</div><div>print("c=\(wrap_inc(a:magic,va<wbr>l:10))")</div></div></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">Any comments/thought on this transformation?</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">Best,</div><div style="font-size:12.8px">Raj </div></div></div></div><span class="">
______________________________<wbr>_________________<br>swift-dev mailing list<br><a href="mailto:swift-dev@swift.org" target="_blank">swift-dev@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-dev" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-dev</a><br></span></div></blockquote></div><br></div></div></blockquote></div><br></div>