Execution of defer in Golang assembly

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.linkpoint Goroutine._deferand then Goroutine._deferpoint to dform a singly linked list, and Goroutine._deferfinally point to the last element of the linked list, and the first d.linkpoint nilto 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._deferpointing will be passed and released _defer, and the Goroutine._deferpointing will also be made Goroutine._defer.link. deferreturn()Will continue to be called until it Goroutine._deferpoints 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 returnfollowing 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 returnfollowing 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

Guess you like

Origin blog.csdn.net/qq_17818281/article/details/114897384