Execution of defer in @Golang assembly
Simple phenomenon
The defer statement will defer the statements that follow it. When the function to which defer belongs is about to return, the deferred statements will be executed in the reverse order of defer, that is to say, the defer statement will be executed last, and the defer statement will be deferred last. The statement is executed first.
test.go
package main
import (
"fmt"
)
func main() {
defer func() {
fmt.Print(1)
}()
defer func() {
fmt.Print(2)
}()
}
// output:21
Why is it so?
text
Old rules, compile the output (abridged, but no effect)
0x0000 00000 (.\test.go:7) TEXT "".main(SB), ABIInternal, $176-0
0x0000 00000 (.\test.go:7) MOVQ TLS, CX
0x0009 00009 (.\test.go:7) PCDATA $0, $-2
0x0009 00009 (.\test.go:7) MOVQ (CX)(TLS*2), CX
0x0010 00016 (.\test.go:7) PCDATA $0, $-1
0x0010 00016 (.\test.go:7) LEAQ -48(SP), AX
0x0015 00021 (.\test.go:7) CMPQ AX, 16(CX)
0x0019 00025 (.\test.go:7) PCDATA $0, $-2
0x0019 00025 (.\test.go:7) JLS 205
0x001f 00031 (.\test.go:7) PCDATA $0, $-1
0x001f 00031 (.\test.go:7) SUBQ $176, SP
0x0026 00038 (.\test.go:7) MOVQ BP, 168(SP)
0x002e 00046 (.\test.go:7) LEAQ 168(SP), BP
0x0036 00054 (.\test.go:7) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0036 00054 (.\test.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0036 00054 (.\test.go:8) MOVL $0, ""..autotmp_0+88(SP)
0x003e 00062 (.\test.go:8) LEAQ "".main.func1·f(SB), AX
0x0045 00069 (.\test.go:8) MOVQ AX, ""..autotmp_0+112(SP)
0x004a 00074 (.\test.go:8) LEAQ ""..autotmp_0+88(SP), AX
0x004f 00079 (.\test.go:8) MOVQ AX, (SP)
0x0053 00083 (.\test.go:8) PCDATA $1, $0
0x0053 00083 (.\test.go:8) CALL runtime.deferprocStack(SB)
0x0058 00088 (.\test.go:8) TESTL AX, AX
0x005a 00090 (.\test.go:8) JNE 183
0x005c 00092 (.\test.go:8) JMP 94
0x005e 00094 (.\test.go:11) MOVL $0, ""..autotmp_1+8(SP)
0x0066 00102 (.\test.go:11) LEAQ "".main.func2·f(SB), AX
0x006d 00109 (.\test.go:11) MOVQ AX, ""..autotmp_1+32(SP)
0x0072 00114 (.\test.go:11) LEAQ ""..autotmp_1+8(SP), AX
0x0077 00119 (.\test.go:11) MOVQ AX, (SP)
0x007b 00123 (.\test.go:11) NOP
0x0080 00128 (.\test.go:11) CALL runtime.deferprocStack(SB)
0x0085 00133 (.\test.go:11) TESTL AX, AX
0x0087 00135 (.\test.go:11) JNE 161
0x0089 00137 (.\test.go:11) JMP 139
0x008b 00139 (.\test.go:14) XCHGL AX, AX
0x008c 00140 (.\test.go:14) CALL runtime.deferreturn(SB)
0x0091 00145 (.\test.go:14) MOVQ 168(SP), BP
0x0099 00153 (.\test.go:14) ADDQ $176, SP
0x00a0 00160 (.\test.go:14) RET
0x00a1 00161 (.\test.go:11) XCHGL AX, AX
0x00a2 00162 (.\test.go:11) CALL runtime.deferreturn(SB)
0x00a7 00167 (.\test.go:11) MOVQ 168(SP), BP
0x00af 00175 (.\test.go:11) ADDQ $176, SP
0x00b6 00182 (.\test.go:11) RET
0x00b7 00183 (.\test.go:8) XCHGL AX, AX
0x00b8 00184 (.\test.go:8) CALL runtime.deferreturn(SB)
0x00bd 00189 (.\test.go:8) MOVQ 168(SP), BP
0x00c5 00197 (.\test.go:8) ADDQ $176, SP
0x00cc 00204 (.\test.go:8) RET
runtime.deferprocStack()
runtime.dederreturn()
Not surprisingly, it is related to these two functions. I won’t post the source code and check it by myself. I will talk about
https://github.com/golang/go/blob/master/src/runtime/panic.go
deferprocStack(d *_defer) // deferprocStack queues a new deferred function with a defer record on the stack
_defer
The information of a delay function is recorded. For specific information, please check the source code by yourself, which will not be explained here.
deferprocStack()
Roughly, first d.link
point Goroutine._defer
and then Goroutine._defer
point to d
form a singly linked list, and Goroutine._defer
finally point to the last element of the linked list, and the first d.link
point nil
to form a delay function stack.
deferreturn(arg0 uintptr) // is called again and again until there are no more deferred functions.
deferreturn()
The runOpenDeferFrame()
delay function will be executed by executing , freedefer()
and the jmpdefer()
current Goroutine._defer
pointing will be passed and released _defer
, and the Goroutine._defer
pointing will also be made Goroutine._defer.link
. deferreturn()
Will continue to be called until it Goroutine._defer
points to nil
.
Add a meal
I saw it on the public
account of fried fish, haha test.go
package main
func f1() (r int) {
defer func() {
r++
}()
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
func main() {
println(f1())
println(f2())
println(f3())
}
output:
1
5
1
Old rules, compile the output (abridged, but no effect)
f1()
"".f1 STEXT size=146 args=0x8 locals=0x60 funcid=0x0
0x0000 00000 (.\test.go:3) TEXT "".f1(SB), ABIInternal, $96-8 // 可以知道返回值r存放的区域应该是104SP
0x0000 00000 (.\test.go:3) MOVQ TLS, CX
0x0009 00009 (.\test.go:3) PCDATA $0, $-2
0x0009 00009 (.\test.go:3) MOVQ (CX)(TLS*2), CX
0x0010 00016 (.\test.go:3) PCDATA $0, $-1
0x0010 00016 (.\test.go:3) CMPQ SP, 16(CX)
0x0014 00020 (.\test.go:3) PCDATA $0, $-2
0x0014 00020 (.\test.go:3) JLS 136
0x0016 00022 (.\test.go:3) PCDATA $0, $-1
0x0016 00022 (.\test.go:3) SUBQ $96, SP
0x001a 00026 (.\test.go:3) MOVQ BP, 88(SP)
0x001f 00031 (.\test.go:3) LEAQ 88(SP), BP
0x0024 00036 (.\test.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0024 00036 (.\test.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0024 00036 (.\test.go:3) MOVQ $0, "".r+104(SP) // 首先初始化r 赋值为0
0x002d 00045 (.\test.go:4) MOVL $8, ""..autotmp_1+8(SP)
0x0035 00053 (.\test.go:4) LEAQ "".f1.func1·f(SB), AX
0x003c 00060 (.\test.go:4) MOVQ AX, ""..autotmp_1+32(SP)
0x0041 00065 (.\test.go:4) LEAQ "".r+104(SP), AX
0x0046 00070 (.\test.go:4) MOVQ AX, ""..autotmp_1+80(SP)
0x004b 00075 (.\test.go:4) LEAQ ""..autotmp_1+8(SP), AX
0x0050 00080 (.\test.go:4) MOVQ AX, (SP)
0x0054 00084 (.\test.go:4) PCDATA $1, $0
0x0054 00084 (.\test.go:4) CALL runtime.deferprocStack(SB) // 将延时函数压入栈
0x0059 00089 (.\test.go:4) TESTL AX, AX
0x005b 00091 (.\test.go:4) JNE 120
0x005d 00093 (.\test.go:4) JMP 95
0x005f 00095 (.\test.go:7) MOVQ $0, "".r+104(SP) // 结合代码这里是给r赋值为0,以此推测在函数返回前,会将return 后面的表达式或者变量的值写入返回值对应的内存空间
0x0068 00104 (.\test.go:7) XCHGL AX, AX
0x0069 00105 (.\test.go:7) CALL runtime.deferreturn(SB) // 开始执行延时函数
0x006e 00110 (.\test.go:7) MOVQ 88(SP), BP
0x0073 00115 (.\test.go:7) ADDQ $96, SP
0x0077 00119 (.\test.go:7) RET // 返回
0x0078 00120 (.\test.go:4) XCHGL AX, AX
0x0079 00121 (.\test.go:4) CALL runtime.deferreturn(SB)
0x007e 00126 (.\test.go:4) MOVQ 88(SP), BP
0x0083 00131 (.\test.go:4) ADDQ $96, SP
0x0087 00135 (.\test.go:4) RET
// f1.func1 就是 defer 执行的匿名函数
"".f1.func1 STEXT nosplit size=20 args=0x8 locals=0x0 funcid=0x0
0x0000 00000 (.\test.go:4) TEXT "".f1.func1(SB), NOSPLIT|ABIInternal, $0-8 // 虽然在函数定义时没有传参和返回值,但是从argsize的值可以看出,有参数的传递,根据代码结合来看就是r
0x0000 00000 (.\test.go:4) FUNCDATA $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
0x0000 00000 (.\test.go:4) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
0x0000 00000 (.\test.go:5) MOVQ "".&r+8(SP), AX // 将r的地址赋值给AX,即AX指向了r
0x0005 00005 (.\test.go:5) MOVQ (AX), AX // 取AX所指向的值赋给AX,即AX=0
0x0008 00008 (.\test.go:5) MOVQ "".&r+8(SP), CX // 将r的地址赋值给CX,即CX指向了r
0x000d 00013 (.\test.go:5) INCQ AX // AX自加 ,即AX=1
0x0010 00016 (.\test.go:5) MOVQ AX, (CX) //将AX的值赋值给r
0x0013 00019 (.\test.go:6) RET // 返回
It can be seen that there will be an assignment process before the execution of the delay function, that is, the return
following expressions or variables are written to the memory space of the return value, and if the delay function modifies the value of the memory space of the return value during execution, It will make the return
following expressions or variables useless.
If you are helpful, you can like it. If you like it, you can bookmark it. I write less blog. I'm about to interview. I want to get something out. Hahahaha. Recently, I will write down some things I have encountered, what I learned Will also share