<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                LLVM是什么?當我第一次思考這個問題時,我發現自己缺乏O-LLVM (Obfuscator-LLVM)的知識。我需要利用O-LLVM項目來開發一個工具來混淆Android的Native C/ C++的代碼,同時研究虛擬機保護技術。事實上,O-LLVM是源自LLVM項目。它使用LLVM的來混淆代碼生成過程的中間表達(Intermediate Representation),然后將其送到后端,最后后端生成目標的機器碼。 讓我們看一下LLVM在維基百科的定義: >LLVM編譯器的基礎設施項目是“”模塊化、可重用的編譯器和工具鏈技術的集合”,用于開發編譯器前端和后端。 ## LLVM的歷史 ![Chris Lattner](https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/Start%20From%20Scratch%20To%20Talk%20About%20LLVM/Chrise%20Lattner%20picture.jpeg) 2000年,Chris Lattner進入伊利諾伊大學香檳分校攻讀碩士學位。他是個超級大學霸,平均績點達到4分,在學習之余,他還游歷了美國許多名勝古跡,并反復閱讀《編譯者之書:原理、技術和工具》(*Compilers: Principles, Techniques*)。LLVM項目是在他的導師Vikram Adve的指導下開始的。在他的學習生活中,他的核心研究領域是編譯器,他的碩士論文《LLVM:一個多階段優化的編譯器架構》([***LLVM: an infrastructure for multi-stage optimisation***](https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/Start%20From%20Scratch%20To%20Talk%20About%20LLVM/LLVM-2002-12-LattnerMSThesis.pdf)),在這篇論文中,他提出了一種使用低級表示形式,但同時能表達高級信息的虛擬指令。他將其稱為低層虛擬機,這是LLVM項目的原型(LLVM是低層虛擬機的縮寫,Low Level Virtual Machine)。 他敏銳地意識到當時編譯器(或者稱為解釋器)存在著幾個問題,于是他決心解決它們。 ### 1.1 當時的編譯器存在的問題 從2000年12月開始,LLVM被設計為一組具有定義良好接口的可重用庫\[LA04\]。當時,開放源碼編程語言實現被設計成實現特殊用途的工具,通常被當做一個整體來執行。例如,重用**GCC靜態編譯器**的語法分析器模塊來進行靜態分析或重構就非常困難。雖然腳本語言通常可以將運行時狀態(runtime)和解釋器(interpreter)嵌入到更大的應用程序中,但是這個**運行時狀態**是包含或排除一個整體代碼塊的條件下構建的。沒有重用代碼片段的方法,而且在跨語言的項目實現時難以共享代碼。 除了編譯器本身的組成之外,圍繞流行的編程語言實現的社區通常是兩極分化的:要么是傳統的靜態編譯器(如GCC、Free Pascal和FreeBASIC),要么是以解釋器或JIT(Just-in-time)編譯器的形式提供運行時編譯器(runtime compiler)。但同時支持這兩種功能的語言實現是非常少見的,即使它們都支持,通常也很少共享代碼。 | 存在的問題 | 優化的方向 | | --- | --- | | 中間表達形態(IR - Intermediate Representation)不能兼具通用性和強表達力 | 重新設計一種IR語言既包含低級信息也包含高級信息 | | 整體化、高內聚的設計架構 | 改用模塊化的設計模型 | | 難以用SDK和庫的形式來重用 | 設計一種完美的調用機制 | :-: 表格:LLVM最初的優化的方向 ### 1.2 LLVM都做了些什么? LLVM重新設計了一種中間表達式(IR)。這種新的LLVM虛擬指令提供了低層表示(緊湊表示、各種可用的轉換等)的好處,還提供了高級信息,以支持在鏈接時和鏈接后主動進行過程間優化。特別是,該系統的設計目的是支持運行時優化,甚至在機器的空閑時間進行優化。 總之,LLVM最初是作為一項基礎設施研究而進行開發的,用于研究靜態和動態編程語言的動態編譯技術。 在Chrise Lattner博士期間,他進一步使用GCC作為前端進行語義分析,生成中間格式(IF - Intermediate Format),然后使用LLVM完成優化和代碼生成的工作。至此,Lattner作為一名編譯工程師,開始在編譯器開發的社區里小有名氣。 ### 1.3 LLVM的潛在價值被蘋果公司挖掘 因此,在2005年,蘋果公司雇傭了Lattner,并為他組建了一個團隊,為實現蘋果系統中的各種用途而開發LLVM系統。LLVM是蘋果最新的macOS和iOS開發工具的一個組成部分。自2013年以來,索尼一直在PlayStation 4游戲機的軟件開發工具包(SDK)中使用LLVM的Clang作為前端編譯器。 LLVM最初是低級虛擬機的首字母縮寫(Low Level Virtual Machine)。為了避免混淆,官方已經廢棄掉這個稱呼了,因為LLVM已經發展成為一個覆蓋面更廣的項目了,與大多數開發人員所認為的虛擬機幾乎沒有關系。現在,LLVM已經發展為一個包含LLVM中間表達式(IR)、LLVM調試器、C++標準庫實現(完全支持C++ 11和C++ 14)等的大項目。目前LLVM由LLVM基金會管理。 如今,LLVM一個是比其他編譯器更好的編譯器工具鏈,與GCC相比,它具有以下優點: 1. 在某些平臺中編譯要比GCC快得多,例如,調試模式下編譯Objective-C時,LLVM比GCC快3倍。 2. 在生成抽象語法樹(AST - Abstract Syntax Tree)時,占用的內存僅為GCC的1/5。 3. LLVM調試的調試信息表達更精準、更容易易分析和閱讀。 4. LLVM被設計成一個模塊化庫,更容易嵌入IDE或進行重用。 5. LLVM很靈活,并且比GCC更容易擴展。 LLVM是用C++編寫的,用于編譯時、鏈接時、運行時和“空閑時”優化用任意編程語言編寫的程序。最初只是為了編譯C和C++,可是LLVM的語言無關架構設計已經催生了各種各樣的前端:LLVM支持的語言包括ActionScript, Ada, C#, Common Lisp, Crystal, CUDA, D, Delphi, Fortran, Graphical G Programming Language, Halide, Haskell, Java bytecode, Julia, Kotlin, Lua, Objective-C, OpenGL Shading Language, Pony, Python, R, Ruby, Rust, Scala, Swift, and Xojo。 我讀過Chris Lattner在《開源軟件的架構》([***The Architecture of Open Source Applications***](http://www.aosabook.org/en/index.html))一書中所寫的的文章:[***LLVM***](http://www.aosabook.org/en/llvm.html)。接下來,我將做一些從中摘錄一些關鍵性的筆記來講解LLVM。 ## 2 編譯器的三段式架構 傳統靜態編譯器(與大多數C編譯器一樣)最流行的設計是三段式設計,其主要組件是前端(Frontend)、優化器(Optimiser)和后端(Backend)。前端解析源代碼,檢查錯誤,并構建一個特定語言的抽象語法樹(AST)來表示輸入代碼。AST可以選擇性地轉換為中間表達式,以便用于優化器。之后再經過優化器和后端,最后生成能在機器上運行的機器碼。 ![編譯器的三段式架構](https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/Start%20From%20Scratch%20To%20Talk%20About%20LLVM/Three-Phase%20Compiler%20Design.png) 優化器負責執行各種各樣的轉換,以嘗試改進代碼的運行時間,例如消除冗余代碼,這個過程通常是與前后端無關的。后端(也稱為代碼生成器)將代碼映射到目標指令集。除了生成正確的代碼外,它還可以根據所支持的體系結構的特點,來生成適應該架構的優質代碼。編譯器后端常見的部分包括指令選擇、寄存器分配和指令調度。 這個模型同樣適用于解釋器和JIT編譯器。Java虛擬機(JVM - Java Virtual Machine)也是這個模型的實現,它使用Java字節碼作為前端和優化器之間的接口。 ### 2.1 該模型的優點 這種設計模式的過人之處,體現在當編譯器決定支持多種源語言或目標體系結構時。如果編譯器在其優化器中使用公共代碼表示,那么可以為任何可以編譯到它的語言編寫前端,也可以為任何可以從它編譯的目標編寫后端,如下圖所示。 ![三段式設計的優點](https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/Start%20From%20Scratch%20To%20Talk%20About%20LLVM/RetargetableCompiler.png) 使用這種設計模式有三個優點: * **可重用好。** 由于前端和后端是分離的,當移植一個新的語言源(例如Algol或BASIC)時,只需要實現一個新的前端,而現有的優化器和后端保持不變即可。 * **享受豐富的開發者資源。** 這種設計意味著它支持不止一種源語言和目標(例如Intel cpu、ARM、MIPS),從而為更廣泛的程序員提供服務。如果有更多的開發人員參與到這個項目中,那么就會有更多高質量的代碼產生,這自然會對編譯器帶來更多的增強和改進。 * **有利于分工。** 實現前端所需的技能與優化器和后端所需的技能不同。將它們分開可以使“前端人員”更容易地增強和維護他們的編譯器部分。雖然這是一個社交問題,而不是技術問題,但在實踐中它非常重要,尤其是對于希望盡可能減少貢獻障礙的開源項目。 ### 2.2 目前的編程語言是如何實現編譯過程的? 雖然**編譯器的三段式設計**的好處是顯然的,并且被寫進了我們的教科書里,但實際上它幾乎從未完全的實現過。縱觀開源語言實現,回到在LLVM項目的啟動之初,你會發現Perl、Python、Ruby和Java的實現沒有共享代碼。此外,像Glasgow Haskell Compiler (GHC)和FreeBASIC這樣的項目雖然可以重定向到多個不同的CPU,但是它們的實現非常依賴于某一種特定的編程語言(也就是它們所支持那種語言)。各種用于特殊用途的編譯器技術被用于實現JIT編譯器,用以實現圖像處理、正則表達式、顯卡驅動程序,或者被用于其他需要密集型CPU工作的領域。 這個模型有三個主要的成功案例: **Java虛擬機和 .NET 虛擬機。** 首先是Java虛擬機和.NET虛擬機。這兩個系統都提供了JIT編譯器、運行時支持和定義優良的字節碼格式。這意味著任何可以編譯成字節碼格式的語言(有幾十種格式)都可以利用優化器、JIT、運行時(runtime)。權衡的結果是,Java和.NET在實現**運行時(runtime)**的時候,幾乎沒有提供靈活性:它們都強制JIT編譯、垃圾收集機制和使用非常特殊的對象模型。當編譯與此模型不匹配的語言(如C語言、LLJVM項目)時,這會導致性能低下。 **翻譯成C代碼。** 第二個成功案例可能是最不幸的,但也是重用編譯器技術最流行的方法:將輸入源翻譯成C代碼(或其他語言),并用現有的C編譯器進行處理。這允許重用優化器和代碼生成器,提供良好的靈活性,控制了運行時,并且前端實現人員非常容易理解、實現和維護。但不幸的是,這樣做會阻止異常處理的有效實現,是得調試的體驗變得糟糕,降低編譯速度,并且對于需要保證尾調用(或著C語言不支持的其他特性)的語言可能會有問題。 **GCC編譯器。** 此模型的最后一個成功實現是GCC。GCC支持許多前端和后端,并擁有一個活躍而廣泛的開發者社區。GCC作為一個C編譯器有著悠久的歷史,它支持多個目標,并同時支持多語言實現。隨著時間的推移,GCC社區正在向更純粹而簡潔的設計靠近。從GCC 4.4開始,它為優化器提供了一個新的表示(稱為“GIMPLE元組”),與以前的表達形式相比,與前端的分離程度更高。此外,它的Fortran前端和Ada前端使用一中更簡潔的AST。 這三種成功案例雖然看上去不錯,但實質上它們還是存在著較大的局限性,因為它們被設計為單一而整體的程序。例如,我們不可能將GCC嵌入到其他應用程序中、將GCC用作運行時/JIT編譯器,或者在不引入大部分編譯器的情況下提取和重用GCC的代碼片段。想要使用GCC的C++前端來生成文檔、建立代碼索引、重構代碼或者制作靜態分析工具,我們必須將GCC作為一個整體應用程序來使用。我們需要以XML的形式發送的信息,或者編寫插件來將外部代碼注入GCC進程中。 GCC不能作為可重用的庫的原因有很多,包括大量使用全局變量、弱的常量、設計不佳的數據結構、不斷擴展的代碼庫,以及使用宏來阻止將基本代碼一次編譯成支持多對前端/目標的能力。然而,最難解決的問題源自其早期設計時固有架構問題。具體來說,GCC存在分層問題和抽象泄漏的問題:后端遍歷前端AST來生成調試信息,前端生成后端數據結構,而整個編譯器又依賴于命令行接口設置的全局數據結構。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看