为什么在这种情况下会生成不同的Go汇编代码?

huangapple go评论88阅读模式
英文:

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]bytefunc 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

huangapple
  • 本文由 发表于 2021年7月14日 00:36:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/68366274.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定