go的编译优化问题

如下程序

package main

func main(){
    var a = &struct {
    }{}
    var b = &struct {
    }{}
    if a == b{
        println(a,b)
    }
}

直接run,没有输出。将println(a,b)改为println(&a,&b),就能够正确输出a,b的指针。

编译的时候如果禁止优化,那么println(a,b)也是能够输出的。

禁止优化的汇编代码为

"".main STEXT size=150 args=0x0 locals=0x30
    0x0000 00000 (test.go:4)    TEXT    "".main(SB), $48-0
    0x0000 00000 (test.go:4)    MOVQ    TLS, CX
    0x0009 00009 (test.go:4)    MOVQ    (CX)(TLS*2), CX
    0x0010 00016 (test.go:4)    CMPQ    SP, 16(CX)
    0x0014 00020 (test.go:4)    JLS 140
    0x0016 00022 (test.go:4)    SUBQ    $48, SP
    0x001a 00026 (test.go:4)    MOVQ    BP, 40(SP)
    0x001f 00031 (test.go:4)    LEAQ    40(SP), BP
    0x0024 00036 (test.go:4)    FUNCDATA    $0, gclocals·f6bd6b3389b872033d462029172c8612(SB)
    0x0024 00036 (test.go:4)    FUNCDATA    $1, gclocals·217c3c5f2c7a9e43555fa9a99f0bae4a(SB)
    0x0024 00036 (test.go:4)    FUNCDATA    $3, gclocals·bfec7e55b3f043d1941c093912808913(SB)
    0x0024 00036 (test.go:5)    PCDATA  $2, $1
    0x0024 00036 (test.go:5)    PCDATA  $0, $0
    0x0024 00036 (test.go:5)    LEAQ    ""..autotmp_3+8(SP), AX
    0x0029 00041 (test.go:5)    MOVQ    AX, ""..autotmp_2+32(SP)
    0x002e 00046 (test.go:5)    PCDATA  $2, $0
    0x002e 00046 (test.go:5)    PCDATA  $0, $1
    0x002e 00046 (test.go:5)    MOVQ    AX, "".a+16(SP)
    0x0033 00051 (test.go:7)    PCDATA  $2, $1
    0x0033 00051 (test.go:7)    LEAQ    ""..autotmp_5+8(SP), AX
    0x0038 00056 (test.go:7)    MOVQ    AX, ""..autotmp_4+24(SP)
    0x003d 00061 (test.go:7)    PCDATA  $0, $2
    0x003d 00061 (test.go:7)    MOVQ    AX, "".b+8(SP)
    0x0042 00066 (test.go:9)    PCDATA  $2, $2
    0x0042 00066 (test.go:9)    MOVQ    "".a+16(SP), CX
    0x0047 00071 (test.go:9)    PCDATA  $2, $0
    0x0047 00071 (test.go:9)    CMPQ    CX, AX
    0x004a 00074 (test.go:9)    JEQ 78
    0x004c 00076 (test.go:9)    JMP 138
    0x004e 00078 (test.go:11)   CALL    runtime.printlock(SB)
    0x0053 00083 (test.go:11)   PCDATA  $2, $1
    0x0053 00083 (test.go:11)   PCDATA  $0, $3
    0x0053 00083 (test.go:11)   MOVQ    "".a+16(SP), AX
    0x0058 00088 (test.go:11)   PCDATA  $2, $0
    0x0058 00088 (test.go:11)   MOVQ    AX, (SP)
    0x005c 00092 (test.go:11)   CALL    runtime.printpointer(SB)
    0x0061 00097 (test.go:11)   CALL    runtime.printsp(SB)
    0x0066 00102 (test.go:11)   PCDATA  $2, $1
    0x0066 00102 (test.go:11)   PCDATA  $0, $0
    0x0066 00102 (test.go:11)   MOVQ    "".b+8(SP), AX
    0x006b 00107 (test.go:11)   PCDATA  $2, $0
    0x006b 00107 (test.go:11)   MOVQ    AX, (SP)
    0x006f 00111 (test.go:11)   CALL    runtime.printpointer(SB)
    0x0074 00116 (test.go:11)   CALL    runtime.printnl(SB)
    0x0079 00121 (test.go:11)   CALL    runtime.printunlock(SB)
    0x007e 00126 (test.go:11)   JMP 128
    0x0080 00128 (<unknown line number>)    PCDATA  $2, $-2
    0x0080 00128 (<unknown line number>)    PCDATA  $0, $-2
    0x0080 00128 (<unknown line number>)    MOVQ    40(SP), BP
    0x0085 00133 (<unknown line number>)    ADDQ    $48, SP
    0x0089 00137 (<unknown line number>)    RET
    0x008a 00138 (test.go:9)    JMP 128
    0x008c 00140 (test.go:9)    NOP
    0x008c 00140 (test.go:4)    PCDATA  $0, $-1
    0x008c 00140 (test.go:4)    PCDATA  $2, $-1
    0x008c 00140 (test.go:4)    CALL    runtime.morestack_noctxt(SB)
    0x0091 00145 (test.go:4)    JMP 0

此时直接在栈上分配地址,a,b都是8(SP)的地址,所以肯定是相等的。但是假如加入优化,就变成了下面这样

"".main STEXT nosplit size=1 args=0x0 locals=0x0
    0x0000 00000 (test.go:16)   TEXT    "".main(SB), NOSPLIT, $0-0
    0x0000 00000 (test.go:16)   FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (test.go:16)   FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (test.go:16)   FUNCDATA    $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (<unknown line number>)    RET

什么都没了。

如果if里调用的是fmt或者log.Println(a,b),分配在堆上,runtime.newobject分配的是相同的地址,相等也没问题。

所以现在是不是编译器的优化问题?

已邀请:

lrita

赞同来自: chenqinghe

慎对struct{}{}取地址,因为runtime对其进行优化,跟实际语义产生了一些冲突,小心掉坑里

https://dave.cheney.net/2014/03/25/the-empty-struct

https://github.com/Workiva/go-datastructures/pull/123

Xargin

赞同来自:

是优化的问题,

可以通过 gcflags="-N -l" 来关闭优化,这样你的 if 里的代码就可以执行了

优化后的汇编也可以看到,你代码第 9 行的内容直接被当作无用内容丢弃掉了。

要回复问题请先登录注册