让我们假设一个非常简单的例子:
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
1条答案
按热度按时间ymdaylpp1#
汇编语言的输出对理解这里发生的事情并没有特别的帮助。如果没有编译器的错误,编译器会生成符合语言要求的汇编,并且可以自由地以任何方式进行。所涉及的特定寄存器对本分析并不重要。
该行为与Swift的值类型语义完全匹配。
x
是Int,这是一种值类型。return x
行返回x
的副本。x
的当前值是5,因此被复制以返回。稍后,defer
用10的副本替换x
。x
随后被丢弃。当然,对程序集输出感兴趣是没有问题的,在这种情况下,它实际上是有启发性的。Here is the same code(带有优化):
你会注意到10是无处可寻。