Swift延迟语句内部

w80xi6nr  于 2023-02-03  发布在  Swift
关注(0)|答案(1)|浏览(102)

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

func square() -> Int {
    var x = 5
    defer { x = 10 }
    return x
}

为什么返回5?

我们知道defer只能在rbp消失之前工作,所以defer一直执行到return。
组装时的样子:

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

我理解得对吗,示例中的defer和return语句只是使用了不同的寄存器。eax用于return,rdi用于defer。
当我们使用引用类型时会发生什么

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
ymdaylpp

ymdaylpp1#

汇编语言的输出对理解这里发生的事情并没有特别的帮助。如果没有编译器的错误,编译器会生成符合语言要求的汇编,并且可以自由地以任何方式进行。所涉及的特定寄存器对本分析并不重要。
该行为与Swift的值类型语义完全匹配。x是Int,这是一种值类型。return x行返回x的副本。x的当前值是5,因此被复制以返回。稍后,defer用10的副本替换xx随后被丢弃。
当然,对程序集输出感兴趣是没有问题的,在这种情况下,它实际上是有启发性的。Here is the same code(带有优化)

output.square() -> Swift.Int:
        mov     eax, 5
        ret

你会注意到10是无处可寻。

相关问题