go的編譯優化問題

mooy發表於2018-11-12

如下程式

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 分配的是相同的地址,相等也沒問題。

所以現在是不是編譯器的優化問題?

更多原創文章乾貨分享,請關注公眾號
  • go的編譯優化問題
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章