Swift defer 语句内部结构

Pyr*_*ttt -1 deferred swift

让我们假设一个非常简单的例子:

func square() -> Int {
    var x = 5
    defer { x = 10 }
    return x
}
Run Code Online (Sandbox Code Playgroud)

为什么返回5?

我们知道 defer 只能工作到rbp消失为止。所以推迟执行直到返回。

这就是它在汇编中的样子:

output.square() -> Swift.Int:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     qword ptr [rbp - 8], 0
        mov     qword ptr [rbp - 8], 5
        lea     rdi, [rbp - 8]
        call    ($defer #1 () -> () in output.square() -> Swift.Int)
        mov     eax, 5
        add     rsp, 16
        pop     rbp
        ret

$defer #1 () -> () in output.square() -> Swift.Int:
        push    rbp
        mov     rbp, rsp
        mov     qword ptr [rbp - 8], 0
        mov     qword ptr [rbp - 8], rdi
        mov     qword ptr [rdi], 10
        pop     rbp
        ret
Run Code Online (Sandbox Code Playgroud)

我说得对吗,示例中的 defer 和 return 语句只是使用不同的寄存器。 eax用于返回和rdi延迟。

当我们使用引用类型时会发生什么

output.square() -> output.X:
        push    rbp
        mov     rbp, rsp
        push    r13
        sub     rsp, 24
        mov     qword ptr [rbp - 16], 0
        xor     eax, eax
        mov     edi, eax
        call    (type metadata accessor for output.X)
        mov     r13, rax
        call    (output.X.__allocating_init() -> output.X)
        mov     rdi, rax
        mov     qword ptr [rbp - 24], rdi
        call    swift_retain@PLT
        mov     rax, qword ptr [rbp - 24]
        mov     qword ptr [rbp - 16], rax
        lea     rdi, [rbp - 16]
        call    ($defer #1 () -> () in output.square() -> output.X)
        mov     rdi, qword ptr [rbp - 16]
        call    swift_release@PLT
        mov     rax, qword ptr [rbp - 24]
        add     rsp, 24
        pop     r13
        pop     rbp
        ret

type metadata accessor for output.X:
        lea     rax, [rip + (full type metadata for output.X)+16]
        xor     ecx, ecx
        mov     edx, ecx
        ret

$defer #1 () -> () in output.square() -> output.X:
        push    rbp
        mov     rbp, rsp
        push    r13
        sub     rsp, 24
        mov     qword ptr [rbp - 24], rdi
        mov     qword ptr [rbp - 16], 0
        mov     qword ptr [rbp - 16], rdi
        xor     eax, eax
        mov     edi, eax
        call    (type metadata accessor for output.X)
        mov     r13, rax
        call    (output.X.__allocating_init() -> output.X)
        mov     rcx, rax
        mov     rax, qword ptr [rbp - 24]
        mov     rdi, qword ptr [rax]
        mov     qword ptr [rax], rcx
        call    swift_release@PLT
        add     rsp, 24
        pop     r13
        pop     rbp
        ret
Run Code Online (Sandbox Code Playgroud)

Rob*_*ier 5

汇编语言输出对于理解这里发生的事情并不是特别有帮助。如果没有编译器错误,编译器会生成符合语言要求的程序集,并且可以自由地以任何它喜欢的方式进行。所涉及的具体寄存器对此分析并不重要。

该行为与 Swift 的值类型语义完全匹配。x是一个Int,它是一个值类型。该行return x返回 的副本x。当前值为x5,因此将其复制并返回。后来,用 10 的副本defer替换,然后扔掉。xx

当然,对汇编输出感兴趣是没有问题的。在这种情况下,它实际上可以具有启发性。这是经过优化的相同代码

output.square() -> Swift.Int:
        mov     eax, 5
        ret
Run Code Online (Sandbox Code Playgroud)

您会注意到 10 根本找不到。