英文:
Why is different go-assembler code generated in this case?
问题
在生成汇编代码时注意到了一些奇怪的事情。
第一个代码段生成的汇编代码如下:
".foo STEXT nosplit size=20 args=0x10 locals=0x0 funcid=0x0
0x0000 00000 (main.go:6) TEXT ".foo1(SB), NOSPLIT|ABIInternal, $0-16
0x0000 00000 (main.go:6) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:6) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:6) MOVQ $0, ".b+16(SP)
0x0009 00009 (main.go:15) MOVQ ".v+8(SP), AX
0x000e 00014 (main.go:15) MOVQ AX, ".b+16(SP)
0x0013 00019 (main.go:16) RET
第二个代码段生成的汇编代码如下:
".foo STEXT nosplit size=59 args=0x10 locals=0x10 funcid=0x0
0x0000 00000 (main.go:6) TEXT ".foo(SB), NOSPLIT|ABIInternal, $16-16
0x0000 00000 (main.go:6) SUBQ $16, SP
0x0004 00004 (main.go:6) MOVQ BP, 8(SP)
0x0009 00009 (main.go:6) LEAQ 8(SP), BP
0x000e 00014 (main.go:6) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x000e 00014 (main.go:6) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x000e 00014 (main.go:6) MOVQ $0, ".~r1+32(SP)
0x0017 00023 (main.go:7) MOVQ $0, ".b(SP)
0x001f 00031 (main.go:16) MOVQ ".v+24(SP), AX
0x0024 00036 (main.go:16) MOVQ AX, ".b(SP)
0x0028 00040 (main.go:17) MOVQ ".b(SP), AX
0x002c 00044 (main.go:17) MOVQ AX, ".~r1+32(SP)
0x0031 00049 (main.go:17) MOVQ 8(SP), BP
0x0036 00054 (main.go:17) ADDQ $16, SP
0x003a 00058 (main.go:17) RET
在第二个代码段中,可以看到编译器识别到了一个局部变量。为什么会发生这种情况?为什么会生成如此不同的代码?
这是因为在第一个代码段中,函数foo
的返回值是通过函数参数传递的,而在第二个代码段中,返回值是通过在栈上分配空间来传递的。这种差异可能是由于编译器的优化策略不同导致的。编译器可能根据代码的结构和上下文选择不同的优化方式。
如果你想要更深入地了解这个问题,可以查看生成的汇编代码并进行进一步的分析。
英文:
Noticed strange things when generating assembly code
func foo(v uint64) (b [8]byte) {
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
return b
}
func foo(v uint64) [8]byte {
var b [8]byte
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
return b
}
generated this assembly code
"".foo STEXT nosplit size=20 args=0x10 locals=0x0 funcid=0x0
0x0000 00000 (main.go:6) TEXT "".foo1(SB), NOSPLIT|ABIInternal, $0-16
0x0000 00000 (main.go:6) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:6) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (main.go:6) MOVQ $0, "".b+16(SP)
0x0009 00009 (main.go:15) MOVQ "".v+8(SP), AX
0x000e 00014 (main.go:15) MOVQ AX, "".b+16(SP)
0x0013 00019 (main.go:16) RET
and
"".foo STEXT nosplit size=59 args=0x10 locals=0x10 funcid=0x0
0x0000 00000 (main.go:6) TEXT "".foo(SB), NOSPLIT|ABIInternal, $16-16
0x0000 00000 (main.go:6) SUBQ $16, SP
0x0004 00004 (main.go:6) MOVQ BP, 8(SP)
0x0009 00009 (main.go:6) LEAQ 8(SP), BP
0x000e 00014 (main.go:6) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x000e 00014 (main.go:6) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x000e 00014 (main.go:6) MOVQ $0, "".~r1+32(SP)
0x0017 00023 (main.go:7) MOVQ $0, "".b(SP)
0x001f 00031 (main.go:16) MOVQ "".v+24(SP), AX
0x0024 00036 (main.go:16) MOVQ AX, "".b(SP)
0x0028 00040 (main.go:17) MOVQ "".b(SP), AX
0x002c 00044 (main.go:17) MOVQ AX, "".~r1+32(SP)
0x0031 00049 (main.go:17) MOVQ 8(SP), BP
0x0036 00054 (main.go:17) ADDQ $16, SP
0x003a 00058 (main.go:17) RET
in the second case, you can see that the compiler sees that there is a local variable. why is this happening ?
why is such different code generated?
go version go1.16 windows/amd64
file with asm code from
go tool compile -S mail.go > main.s
https://go.godbolt.org/z/G8K79K48G - small asm code
https://go.godbolt.org/z/Yv853E6P3 - long asm code
答案1
得分: 1
这是func foo(v uint64) [8]byte
和func foo(v uint64) (b [8]byte)
之间的区别:
当你指定返回类型为[8]byte
时,你只是告诉编译器foo
的返回类型。
然而,(b [8]byte)
不仅通过指定返回类型来完成上述操作,还会:
- 分配8个字节的内存
- 声明类型为
[8]byte
的变量b
- 将
b
初始化为填充了零的64位分配空间。
当你使用var b [8]byte
手动复制(b [8]byte)
时,它必须手动执行上述项目符号列表。
0x0000 00000 (main.go:6) SUBQ $16, SP
0x0004 00004 (main.go:6) MOVQ BP, 8(SP)
0x0009 00009 (main.go:6) LEAQ 8(SP), BP
英文:
It is the difference between
func foo(v uint64) [8]byte {
and
func foo(v uint64) (b [8]byte) {
When you specify the return as [8]byte
, you are simply informing the compiler of the return type for foo
.
However, (b [8]byte)
not only does the above by specifying the return type, but also
- allocates 8 bytes of memory
- declares the variable
b
, of type[8]byte
- initializes
b
to the zero-filled allocated 64 bits.
<br>
When you are manually replicating (b [8]byte)
by using
var b [8]byte
It then has to go through the bullet pointed list specified above manually.
0x0000 00000 (main.go:6) SUBQ $16, SP
0x0004 00004 (main.go:6) MOVQ BP, 8(SP)
0x0009 00009 (main.go:6) LEAQ 8(SP), BP
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论