高端网站开发公司有哪些,网页设计图片上加文字,宁波建设银行,沧州网站seo1. 调试环境Go 版本#xff1a;1.19.3Gdb 版本#xff1a;12.1CPU 架构#xff1a;amd64Linux 发行版#xff1a;Ubuntu 22.04Linux 内核版本#xff1a;5.15.0-482. 函数调用参数传递规则版本变化在 Go 中函数间进行调用时#xff0c;主调#xff08;caller#xff09…1. 调试环境Go 版本1.19.3Gdb 版本12.1CPU 架构amd64Linux 发行版Ubuntu 22.04Linux 内核版本5.15.0-482. 函数调用参数传递规则版本变化在 Go 中函数间进行调用时主调caller在调用被调callee函数之前需要先将参数存储到某个地方以供被调者使用。在 Go 1.7 之前函数入参都是存储在堆栈上。主调会先在堆栈上划出一块内存然后将调用参数值存储到这块堆栈上最后调用 call 指令跳转到被调函数执行被调函数执行时会从主调分配的那块堆栈上获取入参值。在 Go 1.7 及以后版本对入参传递方式进行了优化函数入参主要通过寄存器传递寄存器不够用了才会将入参存储到栈上。这个优化将 Go 程序执行效率提高了 5%生成的二进制文件大小减少了 2%。详见 Go 1.17 Release Notes。在 amd64 的架构的 CPU 上Go 使用了 RAX、RBX、RCX、RDI, RSI、R8、R9、R10 和 R11 共计 9 个整数寄存器来存储整型入参同时通过 X0~X14 共计15个浮点数寄存器来存储浮点数入参。这些寄存器的顺序也依次对应函数入参从左到右的顺序。如果入参是结构体递归拆解其成员的类型最终也只会是整型或者浮点型仍可以用寄存器传递其值。3. 验证参数传递规则正确性下面在 Go1.19.3 版本上分别对入参类型为整数、浮点数、数组和结构体时参数的传递方式进行验证。3.1. 注意事项3.1.1. 关闭编译器内联验证的被调函数非常简单如果正常编译会被编译器内联到主调函数里面就不会有参数的传递。为了达到验证效果要关闭编译器内联。有两种方式来关闭编译器内联被调函数前加上 //go:noinline 编译标签。Go 编译命令中加上 -gcflags -l 标志。3.1.2. 打开编译器优化在编译时最好别使用禁止优化的选项即不要在 build 时加 -gcflags -N 标志如果禁止优化入参的传递规则在某些场景下会有点混乱没有理清楚以下示例都是在打开编译器优化的情况下执行的。3.2. 编译汇编代码可用用 Go 自带的 objdump 工具会将二进制反汇编成 plan9 汇编也可以使用 Linux 系统自带的 objdump 工具会将二进制反汇编成 att 汇编两种不同的汇编语言在语法有一点差异但并不会导致入参规则上的变化因此任选一种自已熟悉的汇编语言学习即可本文档使用 plan9 汇编。命令语法go build -o main main.go$GOROOT/pkg/tool/linux_amd64/objdump main main.plan9.asm3.3. 验证整数做为入参整数寄存器有 9 个分别对整数入参个数为小于等于 9 个 和大于 9 个两种情况进行验证。3.3.1. 整数入参为 9 个示例中定义了一个 add 函数有 9 个入参1 个返回值。如下//go:noinline
func add(a, b, c, d, e, f, g, h, i int) int {return abcdefghi
}func main() {add(1, 2, 3, 4, 5, 6, 7, 8, 9)
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/int_less_ten.go... int_less_ten.go:9 0x457c54 b801000000 MOVL $0x1, AX int_less_ten.go:9 0x457c59 bb02000000 MOVL $0x2, BX int_less_ten.go:9 0x457c5e b903000000 MOVL $0x3, CX int_less_ten.go:9 0x457c63 bf04000000 MOVL $0x4, DI int_less_ten.go:9 0x457c68 be05000000 MOVL $0x5, SI int_less_ten.go:9 0x457c6d 41b806000000 MOVL $0x6, R8 int_less_ten.go:9 0x457c73 41b907000000 MOVL $0x7, R9 int_less_ten.go:9 0x457c79 41ba08000000 MOVL $0x8, R10 int_less_ten.go:9 0x457c7f 41bb09000000 MOVL $0x9, R11 int_less_ten.go:9 0x457c85 e896ffffff CALL main.add(SB) 从 main.main 汇编指令 3~11 行可以看到 分别将 0-9 这 9 个数字存储到 RAX、RBX、RCX、RDI, RSI、R8、R9、R10 和 R11 这 9 个寄存器中。在准备好入参后会通过 call 指令跳转到 main.add 函数执行main.add 汇编代码如下TEXT main.add(SB) /home/ubuntu/program/reg_demo/int_less_ten.goint_less_ten.go:5 0x457c20 488d1403 LEAQ 0(BX)(AX*1), DX int_less_ten.go:5 0x457c24 4801d1 ADDQ DX, CX int_less_ten.go:5 0x457c27 4801f9 ADDQ DI, CX int_less_ten.go:5 0x457c2a 4801f1 ADDQ SI, CX int_less_ten.go:5 0x457c2d 4c01c1 ADDQ R8, CX int_less_ten.go:5 0x457c30 4c01c9 ADDQ R9, CX int_less_ten.go:5 0x457c33 4c01d1 ADDQ R10, CX int_less_ten.go:5 0x457c36 498d040b LEAQ 0(R11)(CX*1), AX int_less_ten.go:5 0x457c3a c3 RET 在 main.add 函数中会直接从寄存器中取入参的值并进行计算。汇编代码第二行的指令 LEAQ 0(BX)(AX*1), DX 看着有复杂但其作用就是将 RAX 与 RBX 寄存器的值相加并赋值给 RDX可等价为如下指令MOVQ $0, DX;
ADDQ AX, DX;
ADDQ BX, DX通过 LEAQ 的方式可以一条指令就代替下面 3 条指令执行效率更高。这是编译器优化的结果如果在编译程序时带上了禁止优化 -gcflags -N 标志就会从栈上依次取出参数并相加。验证结果表明当入参为整数时且入参个数小于等于 9 个时全部通过寄存器传递。3.3.2. 整数入参为 10 个示例中定义了一个 add 函数有 10 个入参1 个返回值。如下//go:noinline
func add(a, b, c, d, e, f, g, h, i, j int) int {return abcdefghij
}func main() {add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/int_more_ten.go... int_more_ten.go:9 0x457c74 48c704240a000000 MOVQ $0xa, 0(SP) int_more_ten.go:9 0x457c7c b801000000 MOVL $0x1, AX int_more_ten.go:9 0x457c81 bb02000000 MOVL $0x2, BX int_more_ten.go:9 0x457c86 b903000000 MOVL $0x3, CX int_more_ten.go:9 0x457c8b bf04000000 MOVL $0x4, DI int_more_ten.go:9 0x457c90 be05000000 MOVL $0x5, SI int_more_ten.go:9 0x457c95 41b806000000 MOVL $0x6, R8 int_more_ten.go:9 0x457c9b 41b907000000 MOVL $0x7, R9 int_more_ten.go:9 0x457ca1 41ba08000000 MOVL $0x8, R10 int_more_ten.go:9 0x457ca7 41bb09000000 MOVL $0x9, R11 int_more_ten.go:9 0x457cad e86effffff CALL main.add(SB)从上面编指令第 3 行可以看到主调函数 main.man 会先将入参 10 放到堆栈 SP 处然后 4~12 行依次将剩余的 0-9 九个入参放到寄存器中。在准备好入参后会通过 call 指令跳转到 main.add 函数执行main.add 汇编代码如下TEXT main.add(SB) /home/ubuntu/program/reg_demo/int_more_ten.goint_more_ten.go:5 0x457c20 488d1403 LEAQ 0(BX)(AX*1), DX int_more_ten.go:5 0x457c24 4801d1 ADDQ DX, CX int_more_ten.go:5 0x457c27 4801f9 ADDQ DI, CX int_more_ten.go:5 0x457c2a 4801f1 ADDQ SI, CX int_more_ten.go:5 0x457c2d 4c01c1 ADDQ R8, CX int_more_ten.go:5 0x457c30 4c01c9 ADDQ R9, CX int_more_ten.go:5 0x457c33 4c01d1 ADDQ R10, CX int_more_ten.go:5 0x457c36 4c01d9 ADDQ R11, CX int_more_ten.go:5 0x457c39 488b542408 MOVQ 0x8(SP), DX int_more_ten.go:5 0x457c3e 488d040a LEAQ 0(DX)(CX*1), AX int_more_ten.go:5 0x457c42 c3 RET 在 main.add 的汇编代码第 2~9 行可以看到会直接从寄存器中取前 9 个入参的值并进行加运算将运算结果放到 RCX 寄存器中。然后在第 10 行会先将第 10 个参数从堆栈中取中并放到寄存器 RDX 中最后将 RDX 与 RCX 相加并将结果赋给 RAX并用 RET 指令返回。验证结果表明当入参个数大于 9 个时前 0~9 个是通过寄存器传递剩下的是通过堆栈传递。3.4. 浮点数首先验证入参中有浮点数时是否是通过浮点数寄存器传递再验证入参中同时包含浮点数和整数时是否同时通过浮点数寄存器和整数寄存器进行传递。注不做寄存器上限验证上面的 [验证整数做为入参](#3. 验证参数传递规则正确性) 已经做过了。3.4.1. 入参为浮点数示例中定义了一个 add 函数有 2 个浮点数入参1 个返回值。如下//go:noinline
func add(x, y float64) float64 {return xy
}func main() {_ add(1.1, 2.2)
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/float.go... float.go:9 0x457c54 f20f10053c3b0200 MOVSD_XMM $f64.3ff199999999999a(SB), X0 float.go:9 0x457c5c f20f100d443b0200 MOVSD_XMM $f64.400199999999999a(SB), X1 float.go:9 0x457c64 e8b7ffffff CALL main.add(SB) 从上面汇编指令 3~4 行可以看到 主调函数 main.main 分别将两个浮点数入参存储到 X0 和 X1 两个寄存器中。在准备好入参后会通过 call 指令跳转到 main.add 函数执行main.add 汇编代码如下TEXT main.add(SB) /home/ubuntu/program/reg_demo/float.gofloat.go:5 0x457c20 f20f58c1 ADDSD X1, X0 float.go:5 0x457c24 c3 RET 在被调 main.add 的汇编代码中可以看到直接对寄存器 X0 和 X1 进行计算。验证结果表明当入参为浮点数时入参是通过 X0~X14 寄存器传递的。3.4.2. 入参为浮点数和整数示例中定义了一个 add 函数有两个参数一个为整数一个为浮点数1 个返回值。如下//go:noinline
func add(x int, y float64) int {return x int(y)
}func main() {_ add(1, 2.2)
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/mixed.go... mixed.go:10 0x457c54 b801000000 MOVL $0x1, AX mixed.go:10 0x457c59 f20f1005473b0200 MOVSD_XMM $f64.400199999999999a(SB), X0 mixed.go:10 0x457c61 e8baffffff CALL main.add(SB) 从上面汇编指令 3~4 行可以看到两个参数被分别存储到 RAX 整数寄存器和 X0 浮点数寄存器。准备好入参后会通过 call 指令跳转到 main.add 函数执行main.add 汇编代码如下TEXT main.add(SB) /home/ubuntu/program/reg_demo/mixed.gomixed.go:6 0x457c20 f2480f2cc8 CVTTSD2SIQ X0, CX mixed.go:6 0x457c25 4801c8 ADDQ CX, AX mixed.go:6 0x457c28 c3 RET 在被调 main.add 的汇编代码中可以看到直接对寄存器 RAX 和 X1 进行计算并将结果存入 RAX并返回。验证结果表明当入参同时有整数和浮点数时整数入参通过 RAX、RBX、RCX、RDI, RSI、R8、R9、R10 和 R11 寄存器传递浮点数入参通过 X0~X14 寄存器传递。3.5. 数组验证当数组大小未超过9个时入参是否通过寄存器传递。3.5.1. 入参为大小小于9的数组示例中定义了一个 add 函数有一个类型为数组的参数1 个返回值。如下//go:noinline
func add(x [5]int) int {return x[0] x[3]
}func main() {_ add([5]int{1, 2, 3, 4, 5})
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/array.go... array.go:10 0x457c54 48c7042401000000 MOVQ $0x1, 0(SP) array.go:10 0x457c5c 48c744240802000000 MOVQ $0x2, 0x8(SP) array.go:10 0x457c65 48c744241003000000 MOVQ $0x3, 0x10(SP) array.go:10 0x457c6e 48c744241804000000 MOVQ $0x4, 0x18(SP) array.go:10 0x457c77 48c744242005000000 MOVQ $0x5, 0x20(SP) array.go:10 0x457c80 e89bffffff CALL main.add(SB) 从上面汇编指令 3~7 行可以看到入参被放在堆栈中。准备好入参后会通过 call 指令跳转到 main.add 函数执行main.add 汇编代码如下TEXT main.add(SB) /home/ubuntu/program/reg_demo/array.goarray.go:6 0x457c20 488b442408 MOVQ 0x8(SP), AX array.go:6 0x457c25 4803442420 ADDQ 0x20(SP), AX array.go:6 0x457c2a c3 RET 在被调 main.add 的汇编代码中可以看到从堆栈中取值放到寄存器然后进行计算并返回。验证结果表明当入参的类型为数组时无论数组大小是否超过寄存器数量总是通过堆栈来传递。3.6. 结构体验证重点结构体中只含有整数成员且成员个数小于等于 9 个时结构体如何传递结构体中只含有整数成员且成员个数大于 9 个时结构体如何传递结构体中包含数组成员结构体如何传递结构体中同时包含整数和浮点数成员且整数成员个数小于等于 9 个浮点数成员小于等于 15 个结构体如何传递。结构体中同时包含整数和浮点数成员且整数成员个数小于等于 9 个浮点数成员大于 15 个结构体如何传递。3.6.1. 结构体基本整数成员小于等于9个示例中定义了一个 addStruct 函数有一个类型为 Elem 结构体的参数其下有 3 个 byte 类型的成员6 个 int 类型的成员。如下type Elem struct {a, b, c byted, e, f, g, h, i int
}//go:noinline
func addStruct(e Elem) int {return int(e.a)e.i
}func main() {_ addStruct(Elem{1, 2, 3, 4, 5, 6, 7, 8, 9})
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/struct.go... struct.go:13 0x457c74 b801000000 MOVL $0x1, AX struct.go:13 0x457c79 bb02000000 MOVL $0x2, BX struct.go:13 0x457c7e b903000000 MOVL $0x3, CX struct.go:13 0x457c83 bf04000000 MOVL $0x4, DI struct.go:13 0x457c88 be05000000 MOVL $0x5, SI struct.go:13 0x457c8d 41b806000000 MOVL $0x6, R8 struct.go:13 0x457c93 41b907000000 MOVL $0x7, R9 struct.go:13 0x457c99 41ba08000000 MOVL $0x8, R10 struct.go:13 0x457c9f 41bb09000000 MOVL $0x9, R11 struct.go:13 0x457ca5 e876ffffff CALL main.addStruct(SB)从上面汇编指令 3~11 行可以看到结构体的 9 个成员值分别被放到 9 个整数寄存器中。准备好入参后会通过 call 指令跳转到 main.addStruct 函数执行main.addStruct汇编代码如下 struct_align.go:9 0x457c20 88442408 MOVB AL, 0x8(SP) struct_align.go:9 0x457c24 885c2409 MOVB BL, 0x9(SP) struct_align.go:9 0x457c28 884c240a MOVB CL, 0xa(SP) struct_align.go:9 0x457c2c 48897c2410 MOVQ DI, 0x10(SP) struct_align.go:9 0x457c31 4889742418 MOVQ SI, 0x18(SP) struct_align.go:9 0x457c36 4c89442420 MOVQ R8, 0x20(SP) struct_align.go:9 0x457c3b 4c894c2428 MOVQ R9, 0x28(SP) struct_align.go:9 0x457c40 4c89542430 MOVQ R10, 0x30(SP) struct_align.go:9 0x457c45 4c895c2438 MOVQ R11, 0x38(SP) struct_align.go:10 0x457c4a 0fb64c2408 MOVZX 0x8(SP), CX struct_align.go:10 0x457c4f 4a8d0419 LEAQ 0(CX)(R11*1), AX struct_align.go:10 0x457c53 c3 RET 从上面汇编代码可以看到从寄存器中取出入参值并放到堆栈上需要特别注意的是这里放在堆栈上并不是按地址总线宽度而是以内存对齐的规则来放置的如 abc 这三个变量是 byte 类型在内存对齐的规则中是会放在一个 8 字节的内存中上面 1~3 行汇编代码也印证了这点。验证结果表明当结构体做为入参且结构体成员个数小于 10 个时会通过寄存器传递结构体成员值。同时在被调函数中会将寄存器中的值以内存对齐的方式转存在堆栈上方便后续计算。同理当结构体成员中浮点数个数不超过15个时会通过寄存器传递。3.6.2. 结构体基本整数成员大于 9 个示例中定义了一个 addStruct 函数有一个类型为 Elem 结构体的参数其下有 3 个 byte 类型的成员7 个 int 类型的成员。如下type Elem struct {a, b, c byted, e, f, g, h, i, j int
}//go:noinline
func addStruct(e Elem) int {return int(e.a)e.i
}func main() {_ addStruct(Elem{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/struct_more_ten.go.. struct_more_ten.go:14 0x457c54 48c7042401020300 MOVQ $0x30201, 0(SP) struct_more_ten.go:14 0x457c5c 48c744240804000000 MOVQ $0x4, 0x8(SP) struct_more_ten.go:14 0x457c65 48c744241005000000 MOVQ $0x5, 0x10(SP) struct_more_ten.go:14 0x457c6e 48c744241806000000 MOVQ $0x6, 0x18(SP) struct_more_ten.go:14 0x457c77 48c744242007000000 MOVQ $0x7, 0x20(SP) struct_more_ten.go:14 0x457c80 48c744242808000000 MOVQ $0x8, 0x28(SP) struct_more_ten.go:14 0x457c89 48c744243009000000 MOVQ $0x9, 0x30(SP) struct_more_ten.go:14 0x457c92 48c74424380a000000 MOVQ $0xa, 0x38(SP) struct_more_ten.go:14 0x457c9b 0f1f440000 NOPL 0(AX)(AX*1) struct_more_ten.go:14 0x457ca0 e87bffffff CALL main.addStruct(SB) 从上面汇编指令 3~10 行可以看到结构体的 10 个成员值分别被堆栈中。准备好入参后会通过 call 指令跳转到 main.addStruct 函数执行main.addStruct汇编代码如下TEXT main.addStruct(SB) /home/ubuntu/program/reg_demo/struct_more_ten.gostruct_more_ten.go:10 0x457c20 0fb6442408 MOVZX 0x8(SP), AX struct_more_ten.go:10 0x457c25 4803442438 ADDQ 0x38(SP), AX struct_more_ten.go:10 0x457c2a c3 RET 从上面汇编代码可以看到直接从堆栈中取出入参值进行计算。验证结果表明当结构体做为入参且结构体成员个数大于 9 个时整个结构体的所有成员都通过堆栈传递。同理当结构体成员中浮点数个数超过15个时整个结构体的所有成员也都通过堆栈传递。3.6.3. 结构体成员含有数组时示例中定义了一个 addStruct 函数其入参类型为 Elem 结构体该结构体成员含有数组。如下type Elem struct {a [4]int
}//go:noinline
func addStruct(e Elem) int {return e.a[0] e.a[3]
}func main() {_ addStruct(Elem{a: [4]int{1,2,3,4}})
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/struct_array.go..struct_array.go:13 0x457c54 48c7042401000000 MOVQ $0x1, 0(SP) struct_array.go:13 0x457c5c 48c744240802000000 MOVQ $0x2, 0x8(SP) struct_array.go:13 0x457c65 48c744241003000000 MOVQ $0x3, 0x10(SP) struct_array.go:13 0x457c6e 48c744241804000000 MOVQ $0x4, 0x18(SP) struct_array.go:13 0x457c77 e8a4ffffff CALL main.addStruct(SB) TEXT main.addStruct(SB) /home/ubuntu/program/reg_demo/struct_array.gostruct_array.go:9 0x457c20 488b442408 MOVQ 0x8(SP), AX struct_array.go:9 0x457c25 4803442420 ADDQ 0x20(SP), AX struct_array.go:9 0x457c2a c3 RET 从上面汇编代码中可以看到主调 main.main 在调用 main.addStruct 函数前先将参数存放在堆栈被调会从堆栈中取出参数值进行计算。验证结果表明当结构体成员有数组时只能通过堆栈传递。3.6.4. 结构体整数成员 9 个浮点数成员 15 个示例中定义了一个 addStruct 函数其入参类型为 Elem 结构体该结构体有 9 个整数类型成员15 个浮点数类型成员。如下type Elem struct {a, b, c, d, e, f, g, h, i intj, k, l, m, n, o, p, q, r, s, t, u, v, w, x float64
}//go:noinline
func addStruct(e Elem) int {return e.aint(e.m)
}func main() {_ addStruct(Elem{1, 2, 3, 4, 5, 6, 7, 8, 9, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1, 11.11, 12.12, 13.13, 14.14, 15.15})
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.main(SB) /home/ubuntu/program/reg_demo/struct_mixed.go.. struct_mixed.go:14 0x457d06 f20f100512400200 MOVSD_XMM 0x24012(IP), X0 struct_mixed.go:14 0x457d0e f20f100d12400200 MOVSD_XMM 0x24012(IP), X1 ..struct_mixed.go:14 0x457d7c f2440f10350b400200 MOVSD_XMM 0x2400b(IP), X14 struct_mixed.go:14 0x457d85 b801000000 MOVL $0x1, AX struct_mixed.go:14 0x457d8a bb02000000 MOVL $0x2, BX .. struct_mixed.go:14 0x457db0 41bb09000000 MOVL $0x9, R11 struct_mixed.go:14 0x457db6 e865feffff CALL main.addStruct(SB) TEXT main.addStruct(SB) /home/ubuntu/program/reg_demo/struct_mixed.gostruct_mixed.go:9 0x457c20 4889442408 MOVQ AX, 0x8(SP) struct_mixed.go:9 0x457c25 48895c2410 MOVQ BX, 0x10(SP) ...struct_mixed.go:9 0x457c48 4c895c2448 MOVQ R11, 0x48(SP) struct_mixed.go:9 0x457c4d f20f11442450 MOVSD_XMM X0, 0x50(SP) struct_mixed.go:9 0x457c53 f20f114c2458 MOVSD_XMM X1, 0x58(SP) struct_mixed.go:9 0x457c59 f20f11542460 MOVSD_XMM X2, 0x60(SP) ...struct_mixed.go:9 0x457cbf f2440f11b424c0000000 MOVSD_XMM X14, 0xc0(SP) struct_mixed.go:10 0x457cc9 f20f10442468 MOVSD_XMM 0x68(SP), X0 struct_mixed.go:10 0x457ccf f2480f2cc0 CVTTSD2SIQ X0, AX struct_mixed.go:10 0x457cd4 4803442408 ADDQ 0x8(SP), AX struct_mixed.go:10 0x457cd9 c3 RET 从上面汇编代码中可以看到代码有省略主调 main.main 先将结构体成员值放入寄存器 被调函数执行时再从寄存器上将值取出并计算。验证结果表明结构体中同时包含整数和浮点数成员且整数成员个数小于等于 9 个浮点数成员小于等于 15 个结构体通过寄存器传递。3.6.5. 结构体整数成员 9 个浮点数成员 16 个示例中定义了一个 addStruct 函数其入参类型为 Elem 结构体该结构体有 9 个整数类型成员16 个浮点数类型成员。如下type Elem struct {a, b, c, d, e, f, g, h, i intj, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y float64
}//go:noinline
func addStruct(e Elem) int {return e.aint(e.m)
}func main() {_ addStruct(Elem{1, 2, 3, 4, 5, 6, 7, 8, 9, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1, 11.11, 12.12, 13.13, 14.14, 15.15, 16.16})
}编译程序并将编译成的二进制反汇编成汇编语言。TEXT main.addStruct(SB) /home/ubuntu/program/reg_demo/struct_mixed_more.gostruct_mixed_more.go:10 0x457c20 f20f10442468 MOVSD_XMM 0x68(SP), X0 struct_mixed_more.go:10 0x457c26 f2480f2cc0 CVTTSD2SIQ X0, AX struct_mixed_more.go:10 0x457c2b 4803442408 ADDQ 0x8(SP), AX struct_mixed_more.go:10 0x457c30 c3 RET TEXT main.main(SB) /home/ubuntu/program/reg_demo/struct_mixed_more.go... struct_mixed_more.go:14 0x457c62 48c7042401000000 MOVQ $0x1, 0(SP) struct_mixed_more.go:14 0x457c6a 488d7c2408 LEAQ 0x8(SP), DI struct_mixed_more.go:14 0x457c6f 488d3532410200 LEAQ 0x24132(IP), SI struct_mixed_more.go:14 0x457c76 660f1f840000000000 NOPW 0(AX)(AX*1) struct_mixed_more.go:14 0x457c7f 90 NOPL struct_mixed_more.go:14 0x457c80 48896c24f0 MOVQ BP, -0x10(SP) struct_mixed_more.go:14 0x457c85 488d6c24f0 LEAQ -0x10(SP), BP struct_mixed_more.go:14 0x457c8a e8c9d9ffff CALL 0x455658 struct_mixed_more.go:14 0x457c8f 488b6d00 MOVQ 0(BP), BP struct_mixed_more.go:14 0x457c93 e888ffffff CALL main.addStruct(SB) 从上面汇编代码中可以看到主调 main.main 在调用 main.addStruct 函数前先将结构体值从只读数据段0x24132(IP)处拷贝到堆栈上被调会从堆栈中取出参数值进行计算。验证结果表明结构体中同时包含整数和浮点数成员且整数成员个数小于等于 9 个浮点数成员大于 15 个结构体通过堆栈传递。进一步可得出结论当结构体成员中整数成员超过整数寄存器个数或浮点数成员超过浮点数寄存器个数时都会通过堆栈传递。总结RAX、RBX、RCX、RDI, RSI、R8、R9、R10 和 R11 为整数寄存器这 9 个寄存器的顺序也依次对应函数入参从左到右的顺序。X0~X14 为浮点数寄存器X0、X1、X2 ... 的顺序也依次对应函数入参从左到右的顺序。入参为整数时会优先通过整数寄存器传递当整数寄存器不够时通过堆栈传递。入参为浮点数时会优先通过浮点数寄存器传递当浮点数寄存器不够时通过堆栈传递。入参为数组时只能通过堆栈传递。当结构体做为函数入参时结构体所包含的基本成员要么全部通过寄存器传递要么全部通过栈传递不能一部分通过寄存器传递一部分通过栈传递。入参为结构体时当结构体成员有数组时整个结构体只能通过堆栈传递。入参为结构体时当整数成员小于9个且浮点数成员小于15个时整个结构体通过寄存器传递。入参为结构体时当整数成员大于9个或浮点数成员大于15个时整个结构体通过堆栈传递。