<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                :-: ![Pic. LLVMStucture-overlook][1] :-: 圖1.LLVM的三段式架構 根據我們對傳統編譯器三段式設計的了解,源代碼通常需要經過六個部分,最后生成目標的機器碼: 1. **詞法分析。** 詞法分析將字符序列轉換為記號的序列。執行詞法分析的程序包括了詞法分析器、記號序列化生成器和掃描器,不過掃描器常常作為詞法分析器的第一階段。 2. **語法分析。** 分析符合一定語法規則的一串符號。它通常會生成一個語法樹(或稱為AST - Abstract Syntax tree),用于表示記號之間的語法關系。 3. **語義分析。** 通過語法分析的解析后,這個過程將從源代碼中收集必要的語義信息。它通常包括類型檢查,或者確保在使用之前聲明了變量,這在EBNF范式中是不可能描述的,因此在語法分析階段不容易檢測到。 4. **中間表達式(IR)生成。** 代碼在這個階段會轉換為中間表示式(IR),這是一種中立的語言,與源語言(前端)和機器(后端)無關。 5. **優化中間表達式。** IR代碼常常會有冗余和死代碼的情況出現,而優化器可以處理這些問題以獲得更優異的性能。 6. **生成目標代碼。** 最后后端會生成在目標機器上運行的機器碼,我們也將其稱之為目標代碼。 LLVM結構也遵循這種由6部分的設計。如圖1所示,是LLVM的三段式設計,而圖2展示了三個階段是如何被分為六個部分的。還有值得注意的是。一方面,狹義的LLVM僅包括優化器和后端,也就是負責IR的優化和目標代碼的生成;另一方面,廣義的LLVM是指所有的三個階段、完整工具鏈,以及一整套的SDK編譯器開發技術體系。我們通常稱它為*LLVM集合*。 :-: ![Pic. LLVMStucture-overlook2][2] :-: 圖2.將LLVM拆解成六部分 ## 1 進一步地闡述LLVM模型 如圖所示,新的IR語言是通過優化器連接前端和后端的橋梁。前端將把不同的源語言轉換成中立的IR表達式,它們將在優化器中經過若干的模塊優化后,盡可能達到完美的狀態。然后將優化后的代碼移送到后端,為不同的目標生成機器碼。 ### 1.1 如何寫LLVM的中間表達式(IR)來進行優化 為了讓您對優化如何工作有一些直觀的了解,有必要瀏覽一些示例。因為有許多不同類型的編譯器優化,所以很難提供一個能解決任意問題的方法。但是,大部分的優化遵循一個簡單的三部分結構: * 尋找要轉換的模式。 * 驗證匹配實例的轉換是否安全/正確。 * 進行轉換,更新代碼。 最簡單的優化是算術恒等式的模式匹配,比如:對于任意的整數 `X`,我們會有 * `X-X` = 0 * `X-0` = `X` * `(X*2)-X` = `X` 首先就是上述這些表達式在LLVM IR中是如何表示的,以下是一些例子: ``` ? ? ? %example1 = sub i32 %a, %a ? ? ? %example2 = sub i32 %b, 0 ? ? ? %tmp = mul i32 %c, 2 %example3 = sub i32 %tmp, %c ? ? ? ``` 對于這些類型的窺孔轉換(peephole transformations),LLVM提供了一個指令簡化接口,作為各種其他高級轉換的工具。這些特殊的變換位于`SimplifySubInst`函數中,看起來像這樣: ``` // X - 0 -> X if (match(Op1, m_Zero())) return Op0; // X - X -> 0 if (Op0 == Op1) return Constant::getNullValue(Op0->getType()); // (X*2) - X -> X if (match(Op0, m_Mul(m_Specific(Op1), m_ConstantInt<2>()))) return Op1; … return 0; // Nothing matched, return null to indicate no transformation. ``` 在這段代碼中,Op0和Op1是整數減指令的左右操作數,需要注意的是,這些恒等式不一定適用于IEEE浮點數。LLVM是用C++實現的,而與Objective Caml等函數性語言相比,C++的模式匹配能力并不是特別有名,但它提供了一個通用性良好的模板系統,允許我們實現類似的功能。`match`函數和`m\_`函數允許我們在LLVM IR代碼上執行聲明式模式匹配操作。例如,`m\_Specific`謂詞只有在乘法的左邊與Op1相同時才匹配。 這三種情況都是模式匹配的,如果可以,函數返回替換,如果不可替換,則返回空指針。這個函數的調用者(`SimplifyInstruction`)是一個分發器(dispatcher),它打開指令操作碼,將其分派到每個操作碼的helper函數中。它是通過各種優化調用的。一個簡單的驅動程序是這樣的: ``` for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) if (Value *V = SimplifyInstruction(I)) I->replaceAllUsesWith(V); ``` 這段代碼只是循環遍歷塊中的每條指令,檢查它們是否簡化了。當`SimplifyInstruction`返回非空時,即說明存在未簡化的指令,它將使用`replaceAllUsesWith`方法使用簡單的形式來替代代碼中的任何內容。 ### 1.2 LLVM中間表達式(IR)具有表達的完備性 特別是,LLVM IR是被指定的、優化器的唯一接口。這意味著,要為LLVM編寫前端,只需要知道LLVM IR是什么、它是如何工作的以及它所期望的不變量是什么。由于LLVM IR具有優秀的文本形式,因此可以構建一個前端,將LLVM IR輸出為文本,然后通過Unix管道將其發送到你選擇的一系列優化器和代碼生成器中。 這可能會令你感到驚訝,但這實際上是LLVM的一個非常新穎的特性,也是它在廣泛的應用程序中獲得成功的主要原因之一。即使是非常成功且架構相對良好的GCC編譯器也沒有這個特性:它的GIMPLE中間層表示不是一個自包含的表示。舉一個簡單的例子,當GCC代碼生成器發出DWARF調試信息時,它返回并遍歷源代碼層的“樹”。GIMPLE本身對代碼中的操作使用元組(tuple)表示,但是(至少在GCC 4.5中)仍然將操作數表示為對源代碼層的樹的引用。 這意味著前端作者需要知道并生成GCC的樹數據結構,以及GIMPLE來編寫GCC前端。GCC后端也有類似的問題,所以他們還需要了解RTL后端是如何工作的。最后,GCC沒有辦法以文本形式輸出“表示代碼的所有內容”,也沒有辦法讀寫GIMPLE(以及構成代碼表示形式的相關數據結構)。結果是,GCC的實驗相對困難,因此前端相對較少。 ### 1.3 LLVM是庫的集合 在設計了LLVM IR之后,LLVM的下一個最重要的方面就是將LLVM設計成一系列的庫,而不是像GCC那樣的單一命令行編譯器,或者像JVM或.NET虛擬機那樣的不透明虛擬機。LLVM是一種基礎設施,是一組有用的編譯器技術,可以用于處理特定的問題(比如構建C編譯器,或者在特殊效果管道中構建優化器)。雖然這是它最強大的功能之一,但也是它最不為人知的設計要點之一。 ![Pic. LLVM Structure-passes][3] 看上面的圖,優化器是LLVM的中間過程。它讀取LLVM IR,處理它,然后輸出LLVM IR。我們當然希望它能執行得更快,但是應該怎么做呢?在LLVM(與許多其他編譯器一樣)中,優化器被設計為由若干優化模塊的集合,每個**優化模塊(pass)** 都能讀入IR,完成一些任務后,輸出優化后的IR。常見優化模塊的例子是內聯優化,它會將函數體替換為**調用點(call sites)**,還有,可以將表達式重新組合(expression reassociation)、移動循環不變代碼(*[loop-invariant code motion](https://en.wikipedia.org/wiki/Loop-invariant_code_motion)*)等等。根據優化級別的不同,可以調用不同的優化模塊:例如,Clang編譯器使用`-O0`(無優化狀態)參數進行編譯時不調用pass,在使用`-O3`時將會調用67個pass來進行IR的優化(從LLVM 2.8開始)。 每個LLVM的pass都被寫成C++類,該類間接地繼承自`Pass`類。它們大多數都是寫在一個`.cpp`文件里的,而子類的`Pass`類在一個匿名名稱空間中定義,這使得某一種子類限定在定義它的文件中,是私有的。為了使pass有用,文件外部的代碼必須能夠獲得它,因此需要從文件中導出一個能夠被外部調用的函數。下面是一個簡單的例子: ``` namespace { class Hello : public FunctionPass { public: // Print out the names of functions in the LLVM IR being optimized. virtual bool runOnFunction(Function &F) { cerr << "Hello: " << F.getName() << "\n"; return false; } }; } FunctionPass *createHelloPass() { return new Hello(); } ``` 如前所述,LLVM優化器提供了幾十種不同的優化模塊,每種優化模塊都以類似的風格編寫。這些優化模塊被編譯成一個或多個`.o `文件,然后將其構建到一系列存檔庫中(在Unix系統上是`.a`文件)。這些庫提供了各式各樣的分析和轉換功能,并且優化模塊之間是盡可能松散耦合的:它們應該相互獨立的,或者說,如果某一模塊需要依賴于其他的模塊來完成工作,則應該顯式地聲明它們之間的依賴關系。當給定一系列要運行的優化模塊后,LLVM PassManager會加載這些依賴信息來實現這些依賴項的調用,使得優化模塊能夠正常運行。 雖然庫和抽象功能很好,但它們實際上不能解決所有問題。有趣的是,當有人想要構建一個可以從編譯器技術中獲益的新工具時,可能是一個用于圖像處理語言的JIT編譯器。這個JIT編譯器的實現者考慮到了一些條件:例如,圖像處理語言可能對編譯時延遲非常敏感,并且具有一些特定的語言屬性,而這些屬性對于優化性能非常重要。 基于庫的LLVM優化器的設計,允許我們指定優化模塊的執行順序,以及選擇對圖像處理領域有意義的那些模塊:如果所有內容都定義成一個大函數,那么浪費時間在內聯上就沒有意義了;如果指針很少,別名分析(alias anlysis)和內存優化(memory optimization)就不值得費心。然而,盡管我們盡了最大的努力,LLVM并不能神奇地解決所有優化問題!由于優化模塊的子系統是模塊化的,而PassManager本身對優化模塊的內部機制一無所知,所以實現人員可以自由地實現自己特定于語言的優化模塊,以彌補LLVM優化器中的不足,或者明確地提供特定于語言的優化機會。下圖展示了我們一個假想的XYZ圖像處理系統的簡單例子: ![Pic. PassLinkage][4] 一旦選擇了一組優化(并且為代碼生成器做出了類似的決策),圖像處理編譯器就內置到可執行庫或動態庫中。因為對LLVM的優化模塊的唯一引用是在每個`.o`文件中定義的簡單的`create`函數,而優化器又是在`.a`文件的檔案庫中的,因此只有實際使用的優化模塊才能鏈接到最終應用程序,而不是整個LLVM優化器。在上面的示例中,由于有對PassA和PassB的引用,所以它們將被鏈接進來。因為PassB使用了PassD來做一些分析,所以PassD被鏈接進來。但是,由于沒有使用PassC(以及許多其他優化),所以它的代碼沒有鏈接到進來。 這就是基于庫的LLVM的強大之處。這種簡單的設計方法允許LLVM提供大量的功能,其中一些功能可能只有特定的用戶才會用到,而對于不需要這些功能的人來說,不調用就是了。相比之下,傳統的編譯器優化器是作為緊密連接的代碼塊構建的,這使得子模塊的提取、代碼分析和加速運行變得更加困難。而使用LLVM,你可以單獨弄明白優化器,而不需要知道整個系統是如何組合在一起的。 這種基于庫的設計也是許多人誤解LLVM是什么的原因:LLVM庫有很多功能,但它們自己什么也不做。調用庫(例如Clang C編譯器)的設計人員可以決定如何最好地使用這些代碼塊。這種細致的分層、分類和重視子模塊的能力,正是LLVM優化器可以在不同的環境中,用于如此廣泛的應用程序的原因。而且,雖然LLVM提供了JIT編譯功能,但并不意味著每個客戶機都使用它。 ### 1.4 可重定向的LLVM代碼生成器的設計 LLVM代碼生成器負責將LLVM IR轉換為目標特定的機器碼。一方面來說,代碼生成器的工作是為給定的目標生成盡可能好的機器碼。理想情況下,一個代碼生成器應該對應一個目標,但另一方面,每個目標的代碼生成器需要解決的問題又具有某種相似性。例如,每個目標都需要為**寄存器分配值**,盡管每個目標都有不同的寄存器文件,但是我們應該能設計一種通用的的算法。 與優化器中的方法類似,LLVM的代碼生成器(code generator)將代碼的生成分成若干的模塊(pass)——指令選擇、寄存器分配、調度、代碼布局優化和統一分發——并提供許多默認運行的內置模塊(builtin passes)。然后,代碼生成器的開發者可以自主地選擇默認模塊,重寫模塊,或者可以根據需要,自行編寫用于生成特定目標代碼的模塊。例如,x86的后端使用寄存器減壓調度器,因為它只有很少的寄存器,但是PowerPC后端使用延遲優化調度器,因為它有很多寄存器。x86后端使用自定義模塊來處理x87浮點堆棧,ARM后端使用自定義模塊來將常量池(constant pool islands)放置在需要的函數中。這種靈活性允許目標開發者(target author)生成優秀的代碼,而不必為他們的目標從頭開始編寫整個代碼生成器。 “混合和匹配”方法允許目標作者選擇對其體系結構有意義的內容,并允許在不同目標之間重用大量的代碼。這帶來了另一個挑戰:每個共享組件都需要能夠以通用的方式推斷出目標特定的屬性。例如,共享的寄存器分配器需要知道每個目標的寄存器文件,以及指令與其寄存器操作數之間存在的約束關系。LLVM對此的解決方案是為每個目標提供一個目標描述,這是一種具有聲明屬性的特定域的語言(也就是一組`.td`文件),由tblgen工具負責處理。簡化后的x86目標的構建過程如下圖所示: ![Pic. X86Target][5] `.td文件`所支持的不同子系統允許目標開發者構建目標的不同部分。例如,x86后端定義了一個register類,它包含所有32位寄存器,命名為"GR32"(在`.td`文件中,目標的定義都是大寫的)像這樣: ``` def GR32 : RegisterClass<[i32], 32, [EAX, ECX, EDX, ESI, EDI, EBX, EBP, ESP, R8D, R9D, R10D, R11D, R14D, R15D, R12D, R13D]> { … } ``` 這個定義說這個類中的寄存器可以保存32位整數值("i32"),使用32位對齊,具有指定的16個寄存器(在`.td`文件的其他地方定義),而且還會有更多的信息來指定寄存器的分配順序和其他事情。使用這個定義后,特定的指令可以引用它,將它作為運算對象。例如,“補全32位寄存器”的指令定義為: ``` let Constraints = "$src = $dst" in def NOT32r : I<0xF7, MRM2r, (outs GR32:$dst), (ins GR32:$src), "not{l}\t$dst", [(set GR32:$dst, (not GR32:$src))]>; ``` 這個定義說NOT32r是一條指令(使用`I`tblgen類來表示): * 指定了編碼信息(`0 xf7 MRM2r`) * 定義了輸出是32位寄存器`$dst`,而輸入也是32位寄存器`$src`,`GR32`是上一段代碼定義的寄存器對象,它規定了哪些計寄存器作為運算對象是合法的 * 指定指令的匯編語法,使用`{}`語法同時處理美國電話電報公司(AT&T)和英特爾(Intel)的語法 * 說明指令的效果,并提供最后一行的匹配模式 * 第一行中的"let"約束告訴寄存器分配器,必須將輸入和輸出寄存器分配給相同的物理寄存器。 這個定義是對指令的一種非常密集的描述,公共LLVM代碼可以利用這些信息(通過`tblgen`工具)做很多事情。這一個定義足以使指令選擇器通過對編譯器的輸入IR代碼進行模式匹配來生成這條指令。它還告訴寄存器分配器如何處理它,足以將指令編碼和解碼為機器字節碼(machine code bytes),并足以以文本形式將指令解析和打印出來。這些功能允許一個x86架構的機器支持從目標描述中生成一個獨立的x86匯編程序(它是"gas" GNU匯編程序的一個替代)和反匯編程序,并處理JIT指令的編碼。 除了提供有用的功能之外,從相同的“事實”生成多個信息片段還有其他好處。這種方法使得匯編程序和反匯編程序在匯編語法或二進制編碼上彼此不一致的問題得到解決。它還使目標描述(target description)易于測試:指令的編碼可以進行單元測試,而不需要涉及整個代碼生成器。 盡管我們的期望將`td `文件設計成一種很好的聲明形式,以獲得盡可能多的目標信息,但我們仍然不能解決所有的問題。目前而言,我們還是需要目標機器的開發者編寫一些C++的支持函數(support routines),并實現任何目標機器都會用到的特定模塊(比如`X86FloatingPoint.cpp`,它處理x87浮點堆棧)。隨著LLVM不斷增加對新目標機器的支持,增加可以在`.td`中表示的目標數量變得越來越重要,我們將通過繼續增強`.td`文件的表達能力來實現。相信我們,隨著時間的推移,在LLVM中編寫目標將會變得越來越容易。 ## 2 深入討論模塊化設計 模塊化不僅是一種優雅的設計,還為LLVM庫的使用者提供了一些有趣的功能。這些功能均源于LLVM提供的功能,但是使用者可以自行決定如何使用它。 ### 2.1 模塊化設計給LLVM帶來的一些有意思的能力 如前所述,LLVM IR可以有效地序列化為二進制的形式(被稱為LLVM bitcode),或者從二進制的形式進行反序列化回LLVM IR。由于LLVM IR是自包含(self-contained)的,而且序列化是一個無損過程,所以我們可以進行部分編譯,將進展保存到磁盤,然后在將來的某個時候繼續工作。該特性提供了許多有趣的功能,包括對鏈接間和安裝時優化的支持,這兩個功能都延遲了從“編譯時”生成代碼的時間。 鏈接時間優化(LTO - Link-TIme Optimization)解決了編譯器傳統上只看到一個翻譯單元的問題(例如,一個`.c`文件及它的所有頭文件),因此不能跨文件邊界進行優化,比如內聯(inlining)。像Clang這樣的LLVM編譯器使用`-flto`或`-O4`的命令行選項來支持這一點。此選項指示編譯器向`.o`文件發送LLVM bitcode,而不是寫一個本機對象文件(native object file),并延遲代碼在鏈接時間(link time)的生成,如下圖所示: ![Pic. LTO][6] 具體情況取決于您所使用的操作系統,但重要的是鏈接器檢測到`.o`文件中有LLVM bitcode而不是本機對象文件。當它看到這一點時,它將所有bitcode文件讀入內存,將它們鏈接在一起,然后在一個集合體(over the aggregate)上運行LLVM優化器。由于優化器現在可以一次性掃描很大一部分代碼,因此它可以內聯、傳導常量、進行死代碼消除,以及跨文件邊界執行更多操作。雖然許多現代編譯器都支持LTO,但是它們中的大多數(例如GCC、Open64、Intel編譯器等)都是通過代價高昂而緩慢的序列化過程來實現的。在LLVM中,LTO自然地脫離了系統的設計,并且可以跨不同的源語言(與許多其他編譯器不同)工作,因為IR是一門真正的中立語言(即與前后端無關)。 安裝時優化的概念是延遲代碼生成,甚至比鏈接時間還要晚,直到安裝時間,如下圖所示。安裝時間是一個非常有趣的時間(在軟件裝入一個盒子、下載、上傳到移動設備等情況下),因為這時候你才能知道目標設備的特性。例如,在x86家族中,有各種各樣的芯片和特性。通過延遲指令選擇、調度和代碼生成的其他方面,你可以為應用程序最終運行的特定硬件生成最佳的代碼。 ![Pic. InstallTime][7] ### 2.2 可以對優化器進行單元測試 編譯器非常復雜,而質量很重要,所以測試是至關重要的。例如,在修復了導致優化器崩潰的bug之后,應該添加一個回歸測試,以確保它不會再次發生。傳統的測試方法是編寫一個編譯器中運行`.c`文件,并用一個測試工具來驗證編譯器是否崩潰。例如,這就是GCC測試套件使用的方法。 這種方法的問題是,編譯器由許多不同的子系統組成,甚至優化器中有許多不同的模塊(pass),所有這些子系統都可能會在輸入代碼與產生Bug的節點之間修改過代碼。如果在前端或更早的優化器中發生了更改,測試用例會難以測試它應該測試的內容。 通過使用帶有模塊化優化器的文本形式的LLVM IR,LLVM測試套件注重回歸測試,這些測試可以從磁盤加載LLVM IR,只運行特定的一個模塊(pass),驗證預期的行為。除了崩潰之外,更復雜的行為測試還需要驗證是否確實執行了優化。下面是一個簡單的測試用例,它檢查常量傳播(constant propagation)通過是否使用了add指令: ``` ; RUN: opt < %s -constprop -S | FileCheck %s define i32 @test() { %A = add i32 4, 5 ret i32 %A ; CHECK: @test() ; CHECK: ret i32 9 } ``` RUN那一行的代碼,指定了要執行的命令:在本例中,是`opt`和`FileCheck`命令行工具。`opt`程序是一個簡單的LLVM模塊管理器(pass manager)的封裝,它鏈接所有的標準模塊(并且可以動態加載包含其他模塊的插件),并將它們通過命令行顯示出來。`FileCheck`工具驗證它的標準輸入是否匹配一系列`CHECK`指令。在本例中,這個簡單的測試驗證`constprop`模塊是否將4和5之和折疊(folding)為9。 雖然這看起來是一個非常簡單的例子,但是通過編寫`.c`文件來測試是非常困難的:前端在解析時經常進行**常量折疊**(costant folding),因此編寫代碼來進行后續的常量折疊優化是非常困難和脆弱的。因為我們可以將LLVM IR作為文本加載,并送入我們感興趣的特定優化模塊,然后將結果作為另一個文本文件轉儲出去,所以測試我們真正想要的東西非常簡單,既可以用于回歸測試,也可以用于特性測試。 ### 2.3 根據錯誤點自動減少測試用例 當在LLVM庫的編譯器或其他客戶機中發現Bug時,修復它的第一步是編寫一個能重現問題的測試用例。一旦你有了一個測試用例,最好將其最小化到重現問題的最小示例,并將其縮小到問題發生的LLVM部分。例如,定位到發生故障的那個優化模塊。雖然,最終你將知道如何實現這一點,但是這個過程是冗長的、手工的,對于編譯器生成不正確的代碼但不會崩潰的情況尤其痛苦。 LLVM BugPoint工具使用了IR序列化和LLVM的模塊化設計來自動化這個過程。例如,給定一個輸入`.ll`文件或`.bc`文件,還有一個導致優化器崩潰的模塊列表,BugPoint會將輸入逐步簡化測試用例,直至最小化,最后判斷出哪個優化器有錯誤。然后,輸出簡化后的測試用例和用于重現失敗的`opt`命令。它通過使用類似于“增量調試”(delta debugging)的技術來減少輸入和優化器模塊列表(optimizer pass list),從而發現這一點。因為BugPoint知道LLVM IR的結構,所以它不會像標準的"delta"命令行工具那樣,浪費時間生成要輸入到優化器的無效IR。 在更復雜的錯誤編譯情況下,可以指定輸入、代碼生成器信息,指明需要執行的命令行和輸出的引用(reference output)。BugPoint將首先確定問題是由于優化器還是代碼生成器造成的,然后重復地將測試用例劃分為兩個部分:一個發送到“已知的好”組件中(第一部分),另一個發送到“已知的錯誤”組件中(第二部分)。通過迭代,將會有越來越多的代碼從第二部分中被移除,最終我們會得到一個最小化的測試用例。 BugPoint是一個非常簡單的工具,它在LLVM的整個生命周期中節省了無數的本需要人工去減少測試用例的時間。沒有其他開源編譯器擁有類似的強大工具,因為LLVM依賴于定義良好的中間表示(IR)。但BugPoint并不是完美的,還需要不斷地重寫來改進它。它的歷史可以追溯到2002年,通常只有當某人發現了一個非常棘手的bug,而現有工具又不能很好地處理時,它才會得到改進。Bugpoint隨著時間的推移而發展,在沒有一致的設計或所有者的情況下增加了新特性(比如JIT調試)。 [1]: https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/LLVM%20Implementation%20of%20Three-Phase%20Design/LLVMStucture-overlook.png [2]: https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/LLVM%20Implementation%20of%20Three-Phase%20Design/LLVM%20Structure%20-overlook2.png [3]: https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/LLVM%20Implementation%20of%20Three-Phase%20Design/LLVM%20Structure-passes.png [4]: https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/LLVM%20Implementation%20of%20Three-Phase%20Design/PassLinkage.png [5]: https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/LLVM%20Implementation%20of%20Three-Phase%20Design/X86Target.png [6]: https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/LLVM%20Implementation%20of%20Three-Phase%20Design/LTO.png [7]: https://blog-1252789527.cos.ap-shanghai.myqcloud.com/article/LLVM%20Implementation%20of%20Three-Phase%20Design/InstallTime.png
                  <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>

                              哎呀哎呀视频在线观看