Lets assume very simple example:
func square() -> Int {
var x = 5
defer { x = 10 }
return x
}
Why 5 is returned?
We know that defer is able to work only until rbp
is vanished. So defer executes until return.
That how it look like in assembly:
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
Do I got it right, that defer and return statements in example just use different registers.
eax
for return and rdi
for defer.
And what happens when we use reference type
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