<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">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.<div class=""><br class=""></div><div class=""><br class=""></div><div class="">/ protocol witness for B.foo(_:) in conformance OtherB<br class="">sil shared [transparent] [serialized] [thunk] @_T04test6OtherBCAA1BA2aDP3fooyAA1ACFTW : $@convention(witness_method) (@owned A, @in_guaranteed OtherB) -> () {<br class="">// %0 // user: %7<br class="">// %1 // user: %3<br class="">bb0(%0 : $A, %1 : $*OtherB):<br class=""> %2 = alloc_stack $OtherB // users: %9, %4, %11, %7<br class=""> %3 = load %1 : $*OtherB // users: %6, %4<br class=""> store %3 to %2 : $*OtherB // id: %4<br class=""> // function_ref B.foo(_:)<br class=""> %5 = function_ref @_T04test1BPAAE3fooyAA1ACF : $@convention(method) <τ_0_0 where τ_0_0 : B> (@owned A, @in_guaranteed τ_0_0) -> () // user: %7<br class=""> strong_retain %3 : $OtherB // id: %6<br class=""> %7 = apply %5<OtherB>(%0, %2) : $@convention(method) <τ_0_0 where τ_0_0 : B> (@owned A, @in_guaranteed τ_0_0) -> ()<br class=""> %8 = tuple () // user: %12<br class=""> %9 = load %2 : $*OtherB // user: %10<br class=""> strong_release %9 : $OtherB // id: %10<br class=""> dealloc_stack %2 : $*OtherB // id: %11<br class=""> return %8 : $() // id: %12<br class="">} // end sil function ‘_T04test6OtherBCAA1BA2aDP3fooyAA1ACFTW’</div><div class=""><br class=""></div><div class="">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).</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><a href="https://bugs.swift.org/browse/SR-5403" class="">https://bugs.swift.org/browse/SR-5403</a></div><div class=""><br class=""></div><div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jul 7, 2017, at 11:27 AM, Johannes Weiß via swift-dev <<a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">Hi swift-dev,<br class=""><br class="">If I have basically this program (full program see at the tail end of this mail)<br class=""><br class="">public class A { func bar() { ... }}<br class="">public protocol B {<br class=""> func foo(_ a: A)<br class="">}<br class="">extension B {<br class=""> func foo(_ a: A) { a.bar() }<br class="">}<br class="">public class ActualB: B {<br class="">}<br class="">public class OtherB: B {<br class="">}<br class="">func abc() {<br class=""> let b: B = makeB()<br class=""> b.foo(a)<br class="">}<br class=""><br class="">I get the following call frames when running it (compiled with `swiftc -O -g -o test test.swift`):<br class=""><br class=""> frame #1: 0x0000000100001dbf test`specialized A.bar() at test.swift:6 [opt]<br class=""> frame #2: 0x0000000100001e6f test`specialized B.foo(_:) [inlined] test.SubA.bar() -> () at test.swift:0 [opt]<br class=""> frame #3: 0x0000000100001e6a test`specialized B.foo(a=<unavailable>) at test.swift:23 [opt]<br class=""> frame #4: 0x0000000100001a6e test`B.foo(_:) at test.swift:0 [opt]<br class=""> frame #5: 0x0000000100001b3e test`protocol witness for B.foo(_:) in conformance OtherB at test.swift:0 [opt]<br class=""> frame #6: 0x0000000100001ccd test`abc() at test.swift:45 [opt]<br class=""> frame #7: 0x0000000100001969 test`main at test.swift:48 [opt]<br class=""><br class="">1, 6, and 7 are obviously totally fine and expected.<br class=""><br class="">In 6 we are also building and destroying an existential box, also understandable and fine.<br class=""><br class="">But there's two things I don't quite understand:<br class=""><br class="">I) Why (in 5) will the existential container be retained and released?<br class=""><br class="">--- SNIP ---<br class=""> __T04test6OtherBCAA1BA2aDP3fooyAA1ACFTW: // protocol witness for test.B.foo(test.A) -> () in conformance test.OtherB : test.B in test<br class="">0000000100001b20 push rbp ; CODE XREF=__T04test7ActualBCAA1BA2aDP3fooyAA1ACFTW+4<br class="">0000000100001b21 mov rbp, rsp<br class="">0000000100001b24 push r14<br class="">0000000100001b26 push rbx<br class="">0000000100001b27 mov r14, rdi<br class="">0000000100001b2a mov rbx, qword [r13]<br class="">0000000100001b2e mov rdi, rbx<br class="">0000000100001b31 call _swift_rt_swift_retain<br class="">0000000100001b36 mov rdi, r14 ; argument #1 for method __T04test1BPAAE3fooyAA1ACF<br class="">0000000100001b39 call __T04test1BPAAE3fooyAA1ACF ; (extension in test):test.B.foo(test.A) -> ()<br class="">0000000100001b3e mov rdi, rbx<br class="">0000000100001b41 pop rbx<br class="">0000000100001b42 pop r14<br class="">0000000100001b44 pop rbp<br class="">0000000100001b45 jmp _swift_rt_swift_release<br class=""> ; endp<br class="">--- SNAP ---<br class=""><br class="">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.<br class=""><br class=""><br class="">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).<br class=""><br class=""><br class="">Many thanks,<br class=""> Johannes<br class=""><br class="">--- SNIP ---<br class="">import Darwin<br class=""><br class="">public class A {<br class=""> @inline(never)<br class=""> public func bar() {<br class=""> print("bar")<br class=""> }<br class="">}<br class="">public class SubA: A {<br class=""> @inline(never)<br class=""> public override func bar() {<br class=""> print("bar")<br class=""> }<br class="">}<br class=""><br class="">public protocol B {<br class=""> func foo(_ a: A)<br class="">}<br class=""><br class="">public extension B {<br class=""> @inline(never)<br class=""> func foo(_ a: A) {<br class=""> a.bar()<br class=""> }<br class="">}<br class=""><br class="">public class ActualB: B {<br class="">}<br class=""><br class="">public class OtherB: B {<br class="">}<br class=""><br class="">public func makeB() -> B {<br class=""> if arc4random() == 1231231 {<br class=""> return ActualB()<br class=""> } else {<br class=""> return OtherB()<br class=""> }<br class="">}<br class=""><br class="">@inline(never)<br class="">func abc() {<br class=""> let a = SubA()<br class=""> let b: B = makeB()<br class=""> b.foo(a)<br class="">}<br class=""><br class="">abc()<br class="">--- SNAP ---<br class=""><br class="">_______________________________________________<br class="">swift-dev mailing list<br class=""><a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-dev<br class=""></div></div></blockquote></div><br class=""></div></body></html>