package main
func main() {
var x int32 = 1
nop(x)
}
//go:noinline
func nop(x int32) {}
Go version: go1.16.15 windows/amd64
I wrote above code and compiled it into assembly with go1.16.15 tool compile -S -N -l main.go
for truly understanding Go functions call.
The following is the assembly code of nop function:
"".nop STEXT nosplit size=1 args=0x8 locals=0x0 funcid=0x0
0x0000 00000 (main.go:9) TEXT "".nop(SB), NOSPLIT|ABIInternal, $0-8
0x0000 00000 (main.go:9) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:9) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:9) RET
Obviously, the nop function returns directly without doing anything. It only has a int32
type parameter, but we can see the argsize
is 8 bytes in the assembly:
TEXT “”.nop(SB), NOSPLIT|ABIInternal, $0-8
Question1: Why the argsize
is 8 bytes but not 4 bytes?
The following is the assembly code of main function:
"".main STEXT size=73 args=0x0 locals=0x18 funcid=0x0
0x0000 00000 (main.go:3) TEXT "".main(SB), ABIInternal, $24-0
;; ...omitted stack-split prologue...
0x0016 00022 (main.go:3) SUBQ $24, SP
0x001a 00026 (main.go:3) MOVQ BP, 16(SP)
0x001f 00031 (main.go:3) LEAQ 16(SP), BP
0x0024 00036 (main.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0024 00036 (main.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0024 00036 (main.go:4) MOVL $1, "".x+12(SP)
0x002c 00044 (main.go:5) MOVL $1, (SP)
0x0033 00051 (main.go:5) PCDATA $1, $0
0x0033 00051 (main.go:5) CALL "".nop(SB)
0x0038 00056 (main.go:6) MOVQ 16(SP), BP
0x003d 00061 (main.go:6) ADDQ $24, SP
0x0041 00065 (main.go:6) RET
;; ...omitted stack-split epilogue...
I drawn a picture of stack according to the assembly code:
Question2: I find there are confusing 8 bytes in the stack, why do these 8 bytes exist?
I think memory alignment causes the above phenomenon, but I’m not sure.
Question3: I think the SP
in MOVL $1, "".x+12(SP)
is “pseudo SP”, so why MOVL $1, "".x+12(SP)
but not MOVL $1, "".x-12(SP)
?