首页 > 解决方案 > 为什么在这种情况下会生成不同的 go-assembler 代码?

问题描述

生成汇编代码时注意到奇怪的事情

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
}

生成了这个汇编代码

"".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

在第二种情况下,您可以看到编译器看到有一个局部变量。为什么会这样?为什么会生成如此不同的代码?

go version go1.16 windows/amd64

带有asm代码的文件来自

go tool compile -S mail.go > main.s

https://go.godbolt.org/z/G8K79K48G - 小asm代码

https://go.godbolt.org/z/Yv853E6P3 - 长asm代码

标签: goassemblyx86-64

解决方案


这是之间的区别

func foo(v uint64) [8]byte {

func foo(v uint64) (b [8]byte) {

当您将返回指定为 时[8]byte,您只是将返回类型告知编译器foo

但是,(b [8]byte)上面不仅通过指定返回类型,而且

  • 分配 8 个字节的内存
  • 声明变量b, 类型 [8]byte
  • 初始化b为零填充分配的 64 位。

当您使用手动复制(b [8]byte)

var 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

推荐阅读