編譯器自舉和移植

2021-08-27 04:25:54 字數 3364 閱讀 2162

有個著名的問題:mommy, where do compilers comefrom?要解決這個問題,首先來看看t-diagram。可以將編譯器用乙個t形圖來表示:

---------

| s     t |

---   ---  

| i |

---其中,s表示souce language,t表示target language,i表示implementationlanguage。

根據這個圖,可以得到兩種組合:

1、 由i實現的將s翻譯成a的編譯器和將a翻譯成t的編譯器聯合起來工作,可以實現s到t的編譯。這不是很有意思的組合。

------------------

| s     a | a     t |

---   ------   ---   

| i |    | i |

---      ---

2、由i實現的將s翻譯成t的編譯器(compiler c1),由h實現的將i翻譯成k的編譯器的組合(compilerc2)。這個組合比較有意思:可以用c2去編譯c1(將其實現語言從i翻譯成k),這樣就得到由k實現的將s翻譯成t的編譯器(compilerc3):

---------                ---------

| s     t |              | s     t |

---   ----------  --->   ---   ---

| i | i     k |          | k |

-------   ---            ---

| h |

---這便是我們所感興趣的:編譯器的編譯器。

而乙個程式要在某台機器上執行時,其實現語言必須與相應的機器語言匹配。例如在x86平台上,能執行的程式只能是x86機器語言:

------------------

\    program     /

\              /

-------------

|     x86    |

-------------

|     x86    |

-------------

讓我們將t形圖和上圖聯絡起來看,以c為例(假定已有x86的c編譯器):

--------        --------       ---------

\ hello /        \ hello /       \ hello /

-----------------------        -------

| c   | c    x86| x86  |  --->  | x86  |

---------    ----------        -------

|x86|                 | x86  |

----                  -------

上圖表示用c寫的hello程式可以在c「機器」上執行,然而這不是我們想要的——在x86機器上執行。於是使用實現語言為x86的編譯器(其可執行**)編譯形成x86語言,這樣就可以執行了。

根據上面的第2條(編譯器的編譯器)以及程式的執行,我們可以得出,要想得到在m機器上執行的s語言編譯器,可以使用s語言來編寫(用m語言也可以,然而太麻煩)。但還是需要用m語言寫乙個具有很小功能的s編譯器,然後用其去編譯加入了更多功能的編譯器,然後用編譯出來的更多功能的編譯器去編譯,這樣就得到了最終需要的編譯器,這便是自舉。

---------                ---------

| s     m |              | s     m |

---   ----------  --->   ---   ---

| s | s     m |          | m |

-------   ---            ---

| m |(微型s編譯器)

---用s編寫s的編譯器還有乙個好處,那就是方便移植,並且能產生交叉編譯器。假定要編譯k機器語言的s編譯器,只需要將目標語言替換成k,然後用已有的s語言編譯器來編譯:

---------           ---------

| s     k |         | s     k |

---   ---------- -> ---   ---

| s | s     m |     | m |(執行在m機器上的編譯器,即交叉編譯器)

-------   ---       ---

| m |

---有了交叉編譯器,就可以編譯在k上執行的s編譯器了:

---------           ---------

| s     k |         | s     k |

---   ---------- -> ---   ---

| s | s     k |     | k |

-------   ---       ---

| m |

---

這個知識實在是簡單又基礎,主要是覺得很有意思才在這裡說說。整個gcc(編譯器及交叉編譯器)就是用自舉和移植產生的,對此rms有詳細的說明。——當然,也有很多編譯器不是這樣寫出來的——用另一種語言編寫的。可是,「mom,where does the compiler needed come from?」

實在不願意貼圖了,就用字元代替了,懶:)

compiler construction principles and practice, by kenneth c.louden

"mommy, where do compilers come from?", by anonymous.

copyleft (c) 2007, 2008 raof01.

早期的cpu全部是cisc架構,它的設計目的是要用最少的機器語言指令來完成所需的計算任務。比如對於乘法運算,在cisc架構的cpu上,您可能需要這樣一條指令:muladdra, addrb就可以將addra和addrb中的數相乘並將結果儲存在addra中。將addra,addrb中的資料讀入暫存器,相乘和將結果寫回記憶體的操作全部依賴於cpu中設計的邏輯來實現。這種架構會增加cpu結構的複雜性和對cpu工藝的要求,但對於編譯器的開發十分有利。比如上面的例子,c程式中的a*=b就可以直接編譯為一條乘法指令。今天只有intel及其相容cpu還在使用cisc架構。

risc架構要求軟體來指定各個操作步驟。上面的例子如果要在risc架構上實現,將addra,addrb中的資料讀入暫存器,相乘和將結果寫回記憶體的操作都必須由軟體來實現,比如:mov a, addra; mov b,addrb; mul a, b; str addra,a。這種架構可以降低cpu的複雜性以及允許在同樣的工藝水平下生產出功能更強大的cpu,但對於編譯器的設計有更高的要求。

編譯器自舉

語言的編譯器自舉是某個語言成熟標誌 今天看到乙個新詞 自舉 在知乎上看到輪子哥的解答,很清楚 你想創造一門v語言而且用v語言來寫v編譯器的話,你得按照下面的方法做 1 用c 把那個編譯器 a 寫出來,順便留下很多測試用例。2 用v語言把那個編譯器寫 b 出來,用a.exe來編譯b,修改直到所有測試用...

ucc編譯器(x86移植)

之前寫過一篇ucc的文章,也就是這一篇。這篇文章對ucc的流程說了挺多,但是怎麼把ucc移植到新的cpu上面,卻沒有說很多,後來自己又看了一下 發現還是有不少新的收穫。emit.c檔案是真正的後端入口,所有的彙編檔案的整理 組織部分都是這裡完成的。當然這部分只是框架的內容,告訴我們乙個大概,全域性變...

編譯方舟編譯器

環境 mac os parallels desktop 安裝ubuntu 16.04 安裝基礎包 sudo apt get y install openjdk 8 jdk git core gnupg flex bison gperf build essential zip curl zlib1g ...