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