ABI(Application Binary Interface)x86-64%rax%rdi%rsi%rdx%rcx%r8%r9
GoABIGoABIInternalABI0GoABIInternalwrapper
ABI0ABIInternalGoGomath/bigGoProposal: Create an undefined internal calling convention
Go1.17 Release Notes Compiler5%2%Go1.17Amd64
Go1.18 Release Notes CompilerGOARCH=arm64OARCH=ppc64, ppc64le64ARM64PowerPC10%
Go
ABI0FPABI0ABIInternalABI0ABIInternal

为了测试Go不同版本的调用规约,我们使用下面的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package abi

import "fmt"

//go:noinline
func add(a, b, c, d, e, f, g, h, i, j int) (int, int, int, int, int, int, int, int, int, int) {
sum := a + b + c + d + e + f + g + h + i + j
return sum, 999, 888, 777, 666, 555, 444, 333, 222, 111
}

//go:nosplit
func callAdd() {
fmt.Println(add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
}

测试机的系统信息如下:

Linux ecs-335906 4.18.0-348.7.1.el8_5.x86_64 #1 SMP Wed Dec 22 13:25:12 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

使用下面的指令获取Go的汇编代码:

go tool compile -S -l abi.go

go version go1.17.8 linux/amd64

Go1.17AXBXCXDISIR8R9R10R11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
"".add STEXT nosplit size=88 args=0x58 locals=0x0 funcid=0x0
0x0000 00000 (abi.go:6) TEXT "".add(SB), NOSPLIT|ABIInternal, $0-88
0x0000 00000 (abi.go:6) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (abi.go:6) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (abi.go:6) FUNCDATA $5, "".add.arginfo1(SB)
0x0000 00000 (abi.go:8) MOVQ $111, "".~r19+16(SP) // 返回值10
0x0009 00009 (abi.go:7) LEAQ (BX)(AX*1), DX // 计算 a + b,LEAQ指令的特殊运用,可以用来求和
0x000d 00013 (abi.go:7) ADDQ CX, DX // 计算 a + b + c
0x0010 00016 (abi.go:7) ADDQ DI, DX // 计算 a + b + c + d
0x0013 00019 (abi.go:7) ADDQ SI, DX // 计算 a + b + c + d + e
0x0016 00022 (abi.go:7) ADDQ R8, DX // 计算 a + b + c + d + e + f
0x0019 00025 (abi.go:7) ADDQ R9, DX // 计算 a + b + c + d + e + f + g
0x001c 00028 (abi.go:7) ADDQ R10, DX // 计算 a + b + c + d + e + f + g + h
0x001f 00031 (abi.go:7) ADDQ R11, DX // 计算 a + b + c + d + e + f + g + h + i 放到DX中
0x0022 00034 (abi.go:7) MOVQ "".j+8(SP), R12 // 读取参数10,j,放到R12中
0x0027 00039 (abi.go:7) LEAQ (R12)(DX*1), AX // 计算 DX + j,放到AX中返回
0x002b 00043 (abi.go:8) MOVL $999, BX // 返回值2
0x0030 00048 (abi.go:8) MOVL $888, CX // 返回值3
0x0035 00053 (abi.go:8) MOVL $777, DI // 返回值4
0x003a 00058 (abi.go:8) MOVL $666, SI // 返回值5
0x003f 00063 (abi.go:8) MOVL $555, R8 // 返回值6
0x0045 00069 (abi.go:8) MOVL $444, R9 // 返回值7
0x004b 00075 (abi.go:8) MOVL $333, R10 // 返回值8
0x0051 00081 (abi.go:8) MOVL $222, R11 // 返回值9
0x0057 00087 (abi.go:8) RET

"".callAdd STEXT nosplit size=558 args=0x0 locals=0x148 funcid=0x0
0x0000 00000 (abi.go:12) TEXT "".callAdd(SB), NOSPLIT|ABIInternal, $328-0
0x0000 00000 (abi.go:12) SUBQ $328, SP
0x0007 00007 (abi.go:12) MOVQ BP, 320(SP)
0x000f 00015 (abi.go:12) LEAQ 320(SP), BP
0x0017 00023 (abi.go:12) FUNCDATA $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
0x0017 00023 (abi.go:12) FUNCDATA $1, gclocals·a9e740cbf6936fdd3f94716bd5034c63(SB)
0x0017 00023 (abi.go:12) FUNCDATA $2, "".callAdd.stkobj(SB)
0x0017 00023 (abi.go:13) PCDATA $0, $-2
0x0017 00023 (abi.go:13) MOVQ $10, (SP) // 参数j
0x001f 00031 (abi.go:13) MOVL $1, AX // 参数a
0x0024 00036 (abi.go:13) MOVL $2, BX // 参数b
0x0029 00041 (abi.go:13) MOVL $3, CX // 参数c
0x002e 00046 (abi.go:13) MOVL $4, DI // 参数d
0x0033 00051 (abi.go:13) MOVL $5, SI // 参数e
0x0038 00056 (abi.go:13) MOVL $6, R8 // 参数f
0x003e 00062 (abi.go:13) MOVL $7, R9 // 参数g
0x0044 00068 (abi.go:13) MOVL $8, R10 // 参数h
0x004a 00074 (abi.go:13) MOVL $9, R11 // 参数i
0x0050 00080 (abi.go:13) PCDATA $1, $0
0x0050 00080 (abi.go:13) CALL "".add(SB)
...
add

go version go1.16.15 linux/amd64

可以很明显的看出这个版本中指令较多,参数传递都是通过栈来传递,需要计算的时候再复制到寄存器中进行运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
"".add STEXT nosplit size=179 args=0xa0 locals=0x0 funcid=0x0
0x0000 00000 (abi.go:6) TEXT "".add(SB), NOSPLIT|ABIInternal, $0-160
0x0000 00000 (abi.go:6) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (abi.go:6) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (abi.go:7) MOVQ "".a+8(SP), AX // 参数 a -> AX
0x0005 00005 (abi.go:7) MOVQ "".b+16(SP), CX // 参数 b -> CX
0x000a 00010 (abi.go:7) ADDQ CX, AX // a+b -> AX
0x000d 00013 (abi.go:7) MOVQ "".c+24(SP), CX // 参数 c -> CX
0x0012 00018 (abi.go:7) ADDQ CX, AX // a+b+c -> AX
0x0015 00021 (abi.go:7) MOVQ "".d+32(SP), CX // 参数 d -> CX
0x001a 00026 (abi.go:7) ADDQ CX, AX // a+b+c+d -> AX
0x001d 00029 (abi.go:7) MOVQ "".e+40(SP), CX // 参数 e -> CX
0x0022 00034 (abi.go:7) ADDQ CX, AX // a+b+c+d+e -> AX
0x0025 00037 (abi.go:7) MOVQ "".f+48(SP), CX // 参数 f -> CX
0x002a 00042 (abi.go:7) ADDQ CX, AX // a+b+c+d+e+f -> AX
0x002d 00045 (abi.go:7) MOVQ "".g+56(SP), CX // 参数 g -> CX
0x0032 00050 (abi.go:7) ADDQ CX, AX // a+b+c+d+e+f+g -> AX
0x0035 00053 (abi.go:7) MOVQ "".h+64(SP), CX // 参数 h -> CX
0x003a 00058 (abi.go:7) ADDQ CX, AX // a+b+c+d+e+f+g+h -> AX
0x003d 00061 (abi.go:7) MOVQ "".i+72(SP), CX // 参数 i -> CX
0x0042 00066 (abi.go:7) ADDQ CX, AX // a+b+c+d+e+f+g+h+i -> AX
0x0045 00069 (abi.go:7) MOVQ "".j+80(SP), CX // 参数 j -> CX
0x004a 00074 (abi.go:7) ADDQ CX, AX // a+b+c+d+e+f+g+h+i+j -> AX
0x004d 00077 (abi.go:8) MOVQ AX, "".~r10+88(SP) // 返回值sum
0x0052 00082 (abi.go:8) MOVQ $999, "".~r11+96(SP)
0x005b 00091 (abi.go:8) MOVQ $888, "".~r12+104(SP)
0x0064 00100 (abi.go:8) MOVQ $777, "".~r13+112(SP)
0x006d 00109 (abi.go:8) MOVQ $666, "".~r14+120(SP)
0x0076 00118 (abi.go:8) MOVQ $555, "".~r15+128(SP)
0x0082 00130 (abi.go:8) MOVQ $444, "".~r16+136(SP)
0x008e 00142 (abi.go:8) MOVQ $333, "".~r17+144(SP)
0x009a 00154 (abi.go:8) MOVQ $222, "".~r18+152(SP)
0x00a6 00166 (abi.go:8) MOVQ $111, "".~r19+160(SP)
0x00b2 00178 (abi.go:8) RET

"".callAdd STEXT nosplit size=789 args=0x0 locals=0x190 funcid=0x0
0x0000 00000 (abi.go:12) TEXT "".callAdd(SB), NOSPLIT|ABIInternal, $400-0
0x0000 00000 (abi.go:12) SUBQ $400, SP
0x0007 00007 (abi.go:12) MOVQ BP, 392(SP)
0x000f 00015 (abi.go:12) LEAQ 392(SP), BP
0x0017 00023 (abi.go:12) FUNCDATA $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
0x0017 00023 (abi.go:12) FUNCDATA $1, gclocals·a9e740cbf6936fdd3f94716bd5034c63(SB)
0x0017 00023 (abi.go:12) FUNCDATA $2, "".callAdd.stkobj(SB)
0x0017 00023 (abi.go:13) PCDATA $0, $-2
0x0017 00023 (abi.go:13) MOVQ $1, (SP) // 参数 a
0x001f 00031 (abi.go:13) MOVQ $2, 8(SP) // 参数 b
0x0028 00040 (abi.go:13) MOVQ $3, 16(SP) // 参数 c
0x0031 00049 (abi.go:13) MOVQ $4, 24(SP) // 参数 d
0x003a 00058 (abi.go:13) MOVQ $5, 32(SP) // 参数 e
0x0043 00067 (abi.go:13) MOVQ $6, 40(SP) // 参数 f
0x004c 00076 (abi.go:13) MOVQ $7, 48(SP) // 参数 g
0x0055 00085 (abi.go:13) MOVQ $8, 56(SP) // 参数 h
0x005e 00094 (abi.go:13) MOVQ $9, 64(SP) // 参数 i
0x0067 00103 (abi.go:13) MOVQ $10, 72(SP)// 参数 j
0x0070 00112 (abi.go:13) PCDATA $1, $0
0x0070 00112 (abi.go:13) CALL "".add(SB)
...

栈结构如下图所示:

ABIInternalABI0
GopqABI0Go 1.17Wrapper
main.goasm.smsaGOPATH

go version go1.17.8 linux/amd64
go build -o testmsa -gcflags=“-S -l” msa

相关链接