This is a failure in the optimizer of identifying two loads to return the same value and so it can’t remove a retain/release pair.
/ protocol witness for B.foo(_:) in conformance OtherB sil shared [transparent] [serialized] [thunk] @_T04test6OtherBCAA1BA2aDP3fooyAA1ACFTW : $@convention(witness_method) (@owned A, @in_guaranteed OtherB) -> () { // %0 // user: %7 // %1 // user: %3 bb0(%0 : $A, %1 : $*OtherB): %2 = alloc_stack $OtherB // users: %9, %4, %11, %7 %3 = load %1 : $*OtherB // users: %6, %4 store %3 to %2 : $*OtherB // id: %4 // function_ref B.foo(_:) %5 = function_ref @_T04test1BPAAE3fooyAA1ACF : $@convention(method) <τ_0_0 where τ_0_0 : B> (@owned A, @in_guaranteed τ_0_0) -> () // user: %7 strong_retain %3 : $OtherB // id: %6 %7 = apply %5<OtherB>(%0, %2) : $@convention(method) <τ_0_0 where τ_0_0 : B> (@owned A, @in_guaranteed τ_0_0) -> () %8 = tuple () // user: %12 %9 = load %2 : $*OtherB // user: %10 strong_release %9 : $OtherB // id: %10 dealloc_stack %2 : $*OtherB // id: %11 return %8 : $() // id: %12 } // end sil function ‘_T04test6OtherBCAA1BA2aDP3fooyAA1ACFTW’ If load store forwarding could just tell that the apply does not write to the alloc_stack (It could because @in_guaranteed guarantees no write) … i would expect it to mem promote this … ARC could then remove the retain/release pair (AFAICT). https://bugs.swift.org/browse/SR-5403 <https://bugs.swift.org/browse/SR-5403> > On Jul 7, 2017, at 11:27 AM, Johannes Weiß via swift-dev > <swift-dev@swift.org> wrote: > > Hi swift-dev, > > If I have basically this program (full program see at the tail end of this > mail) > > public class A { func bar() { ... }} > public protocol B { > func foo(_ a: A) > } > extension B { > func foo(_ a: A) { a.bar() } > } > public class ActualB: B { > } > public class OtherB: B { > } > func abc() { > let b: B = makeB() > b.foo(a) > } > > I get the following call frames when running it (compiled with `swiftc -O -g > -o test test.swift`): > > frame #1: 0x0000000100001dbf test`specialized A.bar() at test.swift:6 [opt] > frame #2: 0x0000000100001e6f test`specialized B.foo(_:) [inlined] > test.SubA.bar() -> () at test.swift:0 [opt] > frame #3: 0x0000000100001e6a test`specialized B.foo(a=<unavailable>) at > test.swift:23 [opt] > frame #4: 0x0000000100001a6e test`B.foo(_:) at test.swift:0 [opt] > frame #5: 0x0000000100001b3e test`protocol witness for B.foo(_:) in > conformance OtherB at test.swift:0 [opt] > frame #6: 0x0000000100001ccd test`abc() at test.swift:45 [opt] > frame #7: 0x0000000100001969 test`main at test.swift:48 [opt] > > 1, 6, and 7 are obviously totally fine and expected. > > In 6 we are also building and destroying an existential box, also > understandable and fine. > > But there's two things I don't quite understand: > > I) Why (in 5) will the existential container be retained and released? > > --- SNIP --- > __T04test6OtherBCAA1BA2aDP3fooyAA1ACFTW: // > protocol witness for test.B.foo(test.A) -> () in conformance test.OtherB : > test.B in test > 0000000100001b20 push rbp > ; CODE XREF=__T04test7ActualBCAA1BA2aDP3fooyAA1ACFTW+4 > 0000000100001b21 mov rbp, rsp > 0000000100001b24 push r14 > 0000000100001b26 push rbx > 0000000100001b27 mov r14, rdi > 0000000100001b2a mov rbx, qword [r13] > 0000000100001b2e mov rdi, rbx > 0000000100001b31 call _swift_rt_swift_retain > 0000000100001b36 mov rdi, r14 > ; argument #1 for method __T04test1BPAAE3fooyAA1ACF > 0000000100001b39 call __T04test1BPAAE3fooyAA1ACF > ; (extension in test):test.B.foo(test.A) -> () > 0000000100001b3e mov rdi, rbx > 0000000100001b41 pop rbx > 0000000100001b42 pop r14 > 0000000100001b44 pop rbp > 0000000100001b45 jmp _swift_rt_swift_release > ; endp > --- SNAP --- > > II) Why are 2, 3, 4 and 5 not one stack frame? Seems like we could just JMP > from one to the next. Sure in 5 the call is surrounded by a release/retain > but in the others we could just JMP. > > > We see quite a measurable performance issue in a project we're working on > (email me directly for details/code) and so I thought I'd ask because I'd > like to understand why this is all needed (if it is). > > > Many thanks, > Johannes > > --- SNIP --- > import Darwin > > public class A { > @inline(never) > public func bar() { > print("bar") > } > } > public class SubA: A { > @inline(never) > public override func bar() { > print("bar") > } > } > > public protocol B { > func foo(_ a: A) > } > > public extension B { > @inline(never) > func foo(_ a: A) { > a.bar() > } > } > > public class ActualB: B { > } > > public class OtherB: B { > } > > public func makeB() -> B { > if arc4random() == 1231231 { > return ActualB() > } else { > return OtherB() > } > } > > @inline(never) > func abc() { > let a = SubA() > let b: B = makeB() > b.foo(a) > } > > abc() > --- SNAP --- > > _______________________________________________ > 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