誰說國產編譯器沒救了?這個 C/C++ 和 JavaScript 編譯器來了|程序...

2020-12-20 CSDN

自述 | 楊曉兵

編輯 | 伍杏玲

編者前記:

編譯器是連接人類世界與機器世界之間的一座橋梁,它可將程式設計師理解的高級語言,轉換成程序高效執行的機器碼。在 C/C++ 編譯器裡,有 VC、Borland C++、GCC、Watcom C/C++ 等國外熱門編譯器,但屬於國內自主研發的編譯器較少。

畢竟開發一款實用的編譯器不易,涉及前端詞法、語法分析、語意分析、大量的編譯優化等工作。而有一支團隊,不惜花費十餘年精力完全自主研發出一款 YC 編譯器和 YC 瀏覽器內核。

為何他們不遺餘力地自主研發編譯器和瀏覽器內核?這款編譯器有何優點呢?下面由 YC 編譯器的主要作者之一——楊曉兵,來講述這背後十多年來的漫漫研發路。

以下為楊曉兵自述:

初衷:「做一些對軟體行業進步有幫助的東西」

十多年前,我在中國科學院電子學研究所工作,參與設計一些硬體電路。當時我對硬體的興趣遠超軟體,後創業專門從事軟體工作。

我在創業的過程中發現,做此類軟體雖能賺錢,但無論做得怎樣,對軟體科學的進步都無絲毫作用。儘管付出很多,卻無成就感。

作業系統、資料庫、編譯器以及瀏覽器內核是不需要特殊專業知識的、開發難度非常大、最基礎的軟體產品。

我想從這幾種軟體中選擇其中一項來自主研發,雖然不能肯定做出什麼成就,但我有希望能做出一些對軟體行業進步有所幫助的東西,使自己不枉踏入軟體這個行業。根據當時的情況,我發現可先從瀏覽器內核下手,於是我除了維護原有產品外,把主要精力都投入到瀏覽器的研發中。

創新將 C 代碼內嵌到 HTML

兩年後,我們研發完成瀏覽器內核的基本功能,如 HTML 的解析和顯示、JavaScript 腳本的執行等。

此時,我們發現 HTML 的標準越來越複雜,導致開發難度越來越大,如果按照這樣的發展,瀏覽器內核將無法走入市場。

於是我重新思考:如果把 C 語言處理成像 JavaScript 腳本嵌入到 HTML 中,用內嵌 C 代碼的 HTML 超文本做軟體的人機互動界面,這款內核應該會有點競爭優勢。

於是我們花費兩年半的時間將標準 C 語言以 JavaScript 相似的方式在 HTML 中執行,並擴展了一個 HTML 標籤:<user>,每個 user 標籤都可以用屬性 src 指定一個 C 源碼文件,user標籤的顯示界面和所有行為都由它的 C 代碼決定。

同時將 C 編譯器做成一個函數,用該函數編譯生成 C 程序的可執行代碼,執行代碼可被存入文件或直接執行。此時,我們將編譯器取名為 YC 編譯器,瀏覽器內核取名為 YC 瀏覽器。

三年又三年,漫漫研發路

隨後,我們繼續完善瀏覽器內核,將其中的一些內核代碼獨立出來用內嵌編譯器動態編譯執行,並將大部分內核原始碼開源。

與此同時,我們又遇到一個問題:YC 編譯器雖然編譯速度較快,生成的卻是字節碼,執行速度慢,而且與原生代碼相互調用(特別是回調函數)的處理相當繁瑣。因此用當時的 YC 編譯器難以勝任開原始碼的編譯工作。

為了解決自編譯瀏覽器內核代碼的問題,我們決定修改 YC 編譯器,使它的字節碼轉換為原生的執行碼,並擴展語法,使之具有少量的 C++ 語法。這個工作持續了三年。

三年後,YC 編譯器功能增多,它提供一個函數像調用動態連結庫一樣直接調用 C 源碼中的函數。此時,瀏覽器內核開源部分都可以用 YC 編譯器實時編譯執行了。

我們繼續改進瀏覽器內核,將速度很慢的 JavaScript 字節碼改為二進位原生代碼,使 JavaScript 的執行速度約提高約 100 多倍。同時將瀏覽器內核代碼全部模塊化並開源,每個模塊都用 YC 編譯器動態編譯執行,編譯器的部分源碼也開源(如內嵌彙編編譯器源碼、反彙編源碼、C/C++ 字節碼的執行源碼等),所有的開原始碼均由內嵌的 YC 編譯器自動檢測編譯,動態執行。這個工作大概耗時四年。

開發至此,我想起谷歌和火狐瀏覽器都已開源,為什麼不去看看它們的原始碼呢?於是找到這兩個瀏覽器的源碼。

當時由於一些原因,我分析谷歌瀏覽器源碼沒有編譯通過,而火狐的源碼很順利就編譯成功了,於是我就走上了分析火狐源碼之路。

下載的火狐源碼由純 C 代碼和 C++ 代碼兩部分組成,經 Visual C++ 2013 編譯生成一個 xul.dll 文件和一個 firefox.exe 文件。

我首先分析了它的 C 代碼,將所有的輸出函數全部改為類接口,並讓 xul.dll 通過 YC 編譯器函數 YC_cppLoad() 進行實時編譯,然後用類接口調用 C 源碼中的函數。這一步進行得很順利,若修改了火狐的 C 代碼,只要重新運行火狐瀏覽器便可生效,無需其它操作。

曾經的辦公桌

接下來開始分析火狐 C++ 代碼。YC 編譯器只實現了少數幾個 C++ 語法,不能編譯火狐 C++ 代碼,故分析起來非常困難。

為什麼火狐 C 代碼容易分析,而它的 C++ 代碼難以分析呢?原來我用 YC 編譯器將它的 C 代碼生成彙編代碼文件、變量結構定義文件、宏定義文件和預編譯文件,通過這幾個文件,大大減少了分析難度。

因此我再次決定修改 YC 編譯器,使之完全支持 C++11 標準,因為火狐 C++ 代碼幾乎使用了所有的 C++11 語法特性。先使用 STL 標準模板庫代碼進行編譯器的修改和調試,出乎預料,這個過程竟用了三年時間!

之後,我用 YC++ 編譯器開始調試火狐 C++ 代碼。原以為 STL 那麼複雜的代碼都可以編譯通過並正確執行,火狐 C++ 代碼應該能很快就編譯通過。沒想到,很多語法細節 STL 沒有用到,而火狐 C++ 源碼用到了。於是又繼續修改 YC 編譯器,對火狐 C++ 的各個模塊進行編譯,這個過程持續了一年多

雖然 YC 編譯器可以編譯全部火狐 C++ 代碼,但如何生成執行代碼呢?先從主程序 Firefox.cpp 入手,經整理,這個程序可用 YC 編譯器生成執行代碼 Firefox.exe,並能順利運行。

由於火狐 C++ 各模塊耦合緊密,很難拆分,經過一個多月的工作,仍未能將其拆成多個獨立的源碼模塊以便於用 YC 編譯器實時編譯,動態執行,這也許是我對火狐 C++ 源碼的整體結構還不甚清楚之故,只見其樹木不見其森林。

楊曉兵

當我準備對火狐 C++ 代碼進行再一次總體分析時,有個偶然的機會參與到一個學校管理系統的開發中,因原有的管理系統經常出故障,操作極其不方便。儘管沒有開發 Web 服務程序的經歷,但我做的軟體與 Web 伺服器有極大關係。

經了解,要開發這種管理系統需要的軟體有:Apache 或 Nginx 伺服器,資料庫 MySQL 或其它,編程工具 ASP 或 JSP 或 PHP 等,於是啟發我們自己研發這些工具。YC 的 C/C++ 和 JavaScript 編譯器和 HTML 解析器正好派上用場。

經過一段時間,一個穩定的、可任意擴展的、多線程高並發的 HTTP 伺服器就完成了。該伺服器處理 YSP 文件生成網頁傳給瀏覽器。

YSP 是我設計的與 ASP、JSP 和 PHP 功能相似的一種網頁程式語言。YC 伺服器執行 YSP 文件中的內嵌 C/C++ 或 JavaScript 代碼,生成 HTML 超文本傳給終端設備。工具做好後,不久便做出了管理系統的雛形,這個雛形在發布的 YC 編譯器中可見到。

做了上述這些工作後,我想是時候該寫本書介紹一下 YC 編譯器了,經過一段時間編寫的《YC編譯器—多語言程序設計》(暫名)即將出版。

當我把書完成後,便立即投入64位的C/C++和JavaScript編譯器的開發,目前開發進展順利,已進入測試階段。

編者後記:

三年時間,可將一個呱呱落地的嬰兒變成蹦蹦跳跳的幼兒,可將一名懵懂的職場新人變成沉穩的老兵。而楊曉兵團隊沉下心,迎難而上,花費三年又三年、再一年、兩年、四年的時間只為突破一個個技術難點,最終自研出 YC 編譯器和 YC 瀏覽器內核。

在這過程中,楊曉兵坦言最大的挑戰不僅是技術,還有思維的高度。這期間不僅有大量的研發工作,還為了優化,多次重寫代碼,讓他堅持下來的是想為計算機軟體科學的發展做貢獻的匠心。

目前楊曉兵團隊正在開發 64 位 C/C++ 編譯器,談及未來,楊曉兵表示先在國內推廣,再走向海外。祝福楊曉兵。

YC編譯器傳送門:http://www.ycbro.com

【End】

《原力計劃【第二季】- 學習力挑戰》正式開始!即日起至 3月21日,千萬流量支持原創作者,更有專屬【勳章】等你來挑戰

相關焦點

  • c編譯器so easy,gcc c編譯器生成、使用動靜態庫
    第一章程序開發人員大多接觸過c編譯器,請注意,不要將c編譯器和編輯器弄混淆哦。本文對c編譯器的講解,同樣基於gcc c編譯器,本文主要目的在於對linux環境下gcc c編譯器生成和使用靜態庫和動態庫予以介紹。此外,本文為系列教程第一篇——基本概念篇,之後將帶來另外兩篇。
  • C編譯器小家族之C編譯器各顯神通
    CygwinCygwin是一個Windows下Unix-like模擬環境,具體說就是Unix-like接口(OS API,命令行)重定向層,其目的是不修改軟體源碼僅重新編譯就可以將Unix-like系統上的軟體移植到Windows上(這個移植也許還算不上嚴格意義上的無縫移植)。
  • C 翻身?微軟重寫開源的 C 編譯器!
    「我們把所有對於語言正確性和性能的要求都集中在一份代碼中,使其擁有最佳的質量和最好的多樣性——我們將重新定義「編譯器」這個詞。」Roslyn是C#和Visual Basic.NET的開源編譯器的項目名。
  • C語言編譯器哪個好?6款好用的C語言編譯器推薦
    一些剛開始接觸C語言編譯的網友想下載一款C語言編譯器來使用,不過,網絡上有不少C語言編譯器相關的軟體,讓人很難抉擇。那麼,C語言編譯器哪個好?今天的文章裡,小編給大家整理了6款好用的C語言編譯器推薦給大家,需要下載C語言編譯器的網友,不妨了解一下!
  • 亂花漸欲迷人眼的C編譯器中,誰才是「編譯之王」?
    C語言作為「萬物的始祖」,所有剛接觸的小夥伴都在使用C語言,然而不勝枚舉的C語言編譯器讓人眼花繚亂,VScode、GCC、TCC……那麼到底哪些C語言編譯器是大佬們用的最多的?又有哪些C語言編譯器是兼容性最強的呢?到底誰才是編譯之王?
  • 不下載編譯器,在命令行中執行C sharp代碼
    我相信大家都聽說過java,但是java是哪個公司的大家可能不知道,我也相信大家都聽過C,但是C是哪家公司的大家可能也不知道,而C#是什麼大家可能又不知道了,C#雖然帶個C,但是它和C語言沒有什麼直接關係,C#的正確讀法是C sharp,是微軟模仿java推出的一款語言,在國外C
  • C語言初學者該如何選擇編譯器?哪個編譯器好用?
    剛開始學C語言,很多人都不知道該如何選擇一個編譯器。C語言相對其他程式語言來說,編譯器比較多,網上眾說紛紜,在這裡,我以親身學習經歷說明新手該如何選擇編譯器。學習C語言初期,所需要的是更多的實踐,考慮到初學者的打字能力、編程糾錯能力都有待提升,簡單的編輯器完全能夠滿足初學者需求,還能鍛鍊初學者對程序的理解。
  • 如果你想學C語言,下載哪個編譯器好?
    C語言這個東西現在用的人很少,一般都是用java的多,不過對於我來講,我一看C語言就親切,就想寫,工作上不一定能用得到,但是C語言作為作為所有高級語言的爸爸,多學一點有助於理解一些底層的東西,下面我就跟大家說一下想學C語言下載哪個編譯器好。
  • ARM高效C編程和優化--編譯器,內存和Cache優化以及功耗管理
    關鍵字:ARM Cache 系統 優化 C語言 效率 功耗控制 系統架構 編譯器 efficient NEON本文引用地址:http://www.eepw.com.cn/article/201611/317426.htmC編譯器並非無所不知簡單地說, C編譯器並不能根據程式設計師的代碼就完全理解程式設計師的真實意圖,而且通常為了保證程序的正確執行,通常編譯器會做"最壞的"假設。
  • C語言程序的頭文件一般都是.h結尾,還有些.hpp結尾的是什麼?
    很多C語言學習者多多少少也會了解一些C++的語法,應該能夠發現,C語言和C++程序源文件的後綴名是不同的——C語言程序源文件後綴名為「.c」,而C++程序源文件後綴名一般為「.cpp」,「.h」後綴名則常常是頭文件的後綴名。
  • 世界上第一個C語言編譯器是怎麼編寫的?它為什麼能夠用C語言編寫?
    所謂C語言編譯器,就是把編程得到的文件,比如.c,.h的文件,進行讀取,並對內容進行分析,按照C語言的規則,將其轉換成cpu可以執行的二進位文件。 所以用C語言來做C語言的編譯器是完全可行的。 但是,歷史上的第一個C語言編譯器,肯定不是C語言寫的,因為在沒有編譯器時,無法把C語言轉換成可執行文件。只要有了第一版其它語言的編譯器,就可以用C語言寫編譯器了。
  • 華為方舟編譯器正式支持C語言:完全開源
    2019年8月底,華為方舟編譯器(OpenArkCompiler)正式開源,邁出了跨越性的一步。一年多來,方舟編程體系陸續實現了編譯器、引擎、調試器的開源,其中編譯器的重點功能主要集中在Java應用程式靜態編譯上。
  • C/C+編程筆記:C語言的編譯器工作原理
    1.預處理階段:編譯器以cpp文件作為一個單元,首先讀這個cpp文件,發現第一句與第二句包含一個頭文件,就會在所有搜索路徑中尋找這兩個頭文件,找到之後,就會到相應頭文件中再去處理宏
  • C++typename的由來和用法
    那class使用就夠了,為什麼又引入了新的關鍵詞 typename ,關於這個問題,Stan Lippman 曾在其博客中表示,最早 Stroustrup 使用 來聲明模板參數列表中的類型是為了避免增加不必要的關鍵字;後來委員會認為這樣混用可能造成概念上的混淆才加上了 關鍵字。
  • 搭建C語言開發環境,其實只需要兩個工具就行了!
    在手打代碼之前,我們當然要先搭建自己的開發環境,對於c語言的學習來說,其實只需要兩個工具就行了,那就是文本編輯器和c編譯器了。文本編輯器顧名思義就是你編輯代碼文本的工具,其實和電腦裡自帶記事本一樣,能輸入各種數字、符號、字母,理論上來說,記事本就是一個文本編輯器,只不過因為無法直接對txt文件進行編譯。
  • 東京大學版一生一芯:自製CPU、C編譯器,還成功運行了類Unix系統
    通常來說,CPU、FPU、CPU 模擬器和編譯器都各由一兩個人負責。我負責第 6 組的 CPU 部分。這個實踐課程的有名之處在於對自學能力的高度期望。導師向學生們下達了任務目標:「把這個用 OCaml 寫的光線追蹤程序運行在你們用 FPGA 實現的 CPU 上」,然後就下課了。
  • Linux下C語言編譯的問題
    collect2: ld returned 1 exit status  其根本原因也是找不到test()函數的實現文件,由於該test()函數的實現在test.a這個靜態庫中的,故在連結的時候需要在其後加入test.a這個庫,連結命令修改為如下形式即可。
  • C語言項目中.h和.c文件的關係和概念
    1.通過頭文件來調用庫功能。在很多場合,原始碼不便(或不準)向用戶公布,只要向用戶提供頭文件和二進位的庫即可。用戶只需要按照頭文件中的接口聲明來調用庫功能,而不必關心接口怎麼實現的。編譯器會從庫中提取相應的代碼。 2.頭文件能加強類型安全檢查。如果某個接口被實現或被使用時,其方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這一簡單的規則能大大減輕程式設計師調試、改錯的負擔。
  • 「物聯網」Rust系列3:Rust如何避免C和c++的陷阱
    第一部分:「物聯網」Rust系列1:用Rust重寫了物聯網平臺並成功  第二部分:「物聯網」Rust系列(2):以火取光,C和C++的問題  所以現在我已經徹底,也許不公平烤幾個設計缺陷的一種程式語言超過四十歲,經營著世界上大多數嵌入式設備,讓我們來談談如何鏽設計出這些問題,同時仍然保留了C和c++的部分,讓他們強大的和有用的語言。
  • 還不懂c++vector的用法,你憑什麼勇氣來的!
    今天給大家帶來一篇c++vector的介紹,難以置信這篇文章寫了我三天,不過總算整理完畢,現在分享給大家。模板類vector 和 array是數組的替代品。模板類vector 類似於string類,也是一種動態數組。 在 c++ 中,vector 是一個十分有用的容器。