<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          圖文講解:Go 中的循環(huán)是如何轉(zhuǎn)為匯編的?

          共 5108字,需瀏覽 11分鐘

           ·

          2021-04-28 17:26

          點(diǎn)擊上方藍(lán)色“Go語言中文網(wǎng)”關(guān)注,每天一起學(xué) Go

          Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

          本文基于 Go 1.13 版本

          循環(huán)在編程中是一個(gè)重要的概念,且易于上手。但是,循環(huán)必須被翻譯成計(jì)算機(jī)能理解的底層指令。它的編譯方式也會(huì)在一定程度上影響到標(biāo)準(zhǔn)庫中的其他組件。讓我們開始分析循環(huán)吧。

          循環(huán)的匯編代碼

          使用循壞迭代 arrayslice,channel,以下是一個(gè)使用循環(huán)對(duì) slice 計(jì)算總和的例子。

          func main() {
             l := []int{945236778}
             t := 0

             for _, v := range l {
                t += v
             }

             println(t)
          }

          使用 go tool compile -S main.go 生成的匯編代碼,以下為相關(guān)輸出:

          0x0041 00065 (main.go:4)   XORL   AX, AX
          0x0043 00067 (main.go:4)   XORL   CX, CX

          0x0045 00069 (main.go:7)   JMP    82
          0x0047 00071 (main.go:7)   MOVQ   ""..autotmp_5+16(SP)(AX*8), DX
          0x004c 00076 (main.go:7)   INCQ   AX
          0x004f 00079 (main.go:8)   ADDQ   DX, CX
          0x0052 00082 (main.go:7)   CMPQ   AX, $5
          0x0056 00086 (main.go:7)   JLT    71
          0x0058 00088 (main.go:11)  MOVQ   CX, "".t+8(SP)

          我把這些指令分為了兩個(gè)部分,初始化部分和循環(huán)主體。前兩條指令,將兩個(gè)寄存器初始化為零值。

          0x0041 00065 (main.go:4)   XORL   AX, AX
          0x0043 00067 (main.go:4)   XORL   CX, CX

          寄存器 AX 包含著當(dāng)前循環(huán)所處位置,而 CX 包含著變量 t 的值,下面為帶有指令和通用寄存器的直觀表示:

          循環(huán)從表示「跳轉(zhuǎn)到指令 82 」的 JMP 82 開始,這條指令的作用可以通過第二行來判斷:

          接下來的指令 CMPQ AX,$5 表示「比較寄存器 AX5」,事實(shí)上,這個(gè)操作是把 AX 中的值減去 5 ,然后儲(chǔ)存在另一個(gè)寄存器中,這個(gè)值可以被用在下一條指令 JLT 71 中,它的含義是 「如果值小于 0 則跳轉(zhuǎn)到指令 71 」,以下是更新后的直觀表示:

          如果不滿足條件,則程序?qū)?huì)跳轉(zhuǎn)到循環(huán)體之后的下一條指令執(zhí)行。

          所以,我們現(xiàn)在有了對(duì)循環(huán)的基本框架,以下是轉(zhuǎn)換后的 Go 循環(huán):

          goto end
          start:
             ?
          end:
             if i < 5 {
                goto start
             }

          println(t)

          我們?nèi)鄙倭搜h(huán)的主體,接下來,我們看看這部分的指令:

          0x0047 00071 (main.go:7)   MOVQ   ""..autotmp_5+16(SP)(AX*8), DX
          0x004c 00076 (main.go:7)   INCQ   AX
          0x004f 00079 (main.go:8)   ADDQ   DX, CX

          第一條指令 MOVQ ""..autotmp_5+16(SP)(AX*8), DX  表示 「將內(nèi)存從源位置移動(dòng)到目標(biāo)地址」,它由以下幾個(gè)部分組成:

          • ""..autotmp_5+16(SP) 表示 slice ,而 SP 表示了棧指針即我們當(dāng)前的內(nèi)存空間, autotmp_* 是自動(dòng)生成變量名。
          • 偏差為 8 是因?yàn)樵?64 位計(jì)算機(jī)架構(gòu)中,int 類型是 8 字節(jié)的。偏差乘以寄存器 AX 的值,表示當(dāng)前循環(huán)中的位置。
          • 寄存器 DX 代表的目標(biāo)地址內(nèi)包含著循環(huán)的當(dāng)前值。

          之后,INCQ 表示自增,然后會(huì)增加循環(huán)的當(dāng)前位置:

          循環(huán)主體的最后一條指令是 ADDQ DX, CX ,表示把 DX 的值加在 CX,所以我們可以看出,DX 所包含的值是目前循環(huán)所代表的的值,而 CX 代表了變量 t 的值。

          他會(huì)一直循環(huán)至計(jì)數(shù)器到 5 ,之后循環(huán)體之后的指令表示為將寄存器 CX 的值賦予 t

          0x0058 00088 (main.go:11)   MOVQ   CX, "".t+8(SP)

          以下為最終狀態(tài)的示意圖:

          我們可以完善 Go 中循環(huán)的轉(zhuǎn)換:

          func main() {
             l := []int{945236778}
             t := 0
             i := 0

             var tmp int

             goto end
          start:
             tmp = l[i]
             i++
             t += tmp
          end:
             if i < 5 {
                goto start
             }

             println(t)
          }

          這個(gè)程序生成的匯編代碼與上文所提到的函數(shù)生成的匯編代碼有著相同的輸出。

          改進(jìn)

          循環(huán)的內(nèi)部轉(zhuǎn)換方式可能會(huì)對(duì)其他特性(如 Go 調(diào)度器)產(chǎn)生影響。在 Go 1.10 之前,循環(huán)像下面的代碼一樣編譯:

          func main() {
             l := []int{945236778}
             t := 0
             i := 0

             var tmp int
             p := uintptr(unsafe.Pointer(&l[0]))

             if i >= 5 {
                goto end
             }
          body:
             tmp = *(*int)(unsafe.Pointer(p))
             p += unsafe.Sizeof(l[0])
             i++
             t += tmp
             if i < 5 {
                goto body
             }
          end:
             println(t)
          }

          這種實(shí)現(xiàn)方式的問題是,當(dāng) i 達(dá)到 5 時(shí),指針 p 已經(jīng)超過了內(nèi)存分配空間的尾部。這個(gè)問題使得循環(huán)不容易搶占,因?yàn)樗闹黧w是不安全的。循環(huán)編譯的優(yōu)化確保它不會(huì)創(chuàng)建任何越界的指針。這個(gè)改進(jìn)是為 Go 調(diào)度器中的非合作搶占做準(zhǔn)備的。你可以在這篇 Proposal[1] 中到更詳細(xì)的討論。


          via: https://medium.com/a-journey-with-go/go-how-are-loops-translated-to-assembly-835b985309b3

          作者:Vincent Blanchon[2]譯者:Jun10ng[3]校對(duì):polaris1119[4]

          本文由 GCTT[5] 原創(chuàng)編譯,Go 中文網(wǎng)[6] 榮譽(yù)推出

          參考資料

          [1]

          Proposal: https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md

          [2]

          Vincent Blanchon: https://medium.com/@blanchon.vincent

          [3]

          Jun10ng: https://github.com/Jun10ng

          [4]

          polaris1119: https://github.com/polaris1119

          [5]

          GCTT: https://github.com/studygolang/GCTT

          [6]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲??;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 65
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  98国产精品 | 操骚穴视频在线观看 | 人妖av| 大香蕉久久伊人网 | 欧美高清爱爱视频 |