让我们假设一个非常简单的例子:
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)
汇编语言输出对于理解这里发生的事情并不是特别有帮助。如果没有编译器错误,编译器会生成符合语言要求的程序集,并且可以自由地以任何它喜欢的方式进行。所涉及的具体寄存器对此分析并不重要。
该行为与 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 根本找不到。
| 归档时间: |
|
| 查看次数: |
74 次 |
| 最近记录: |