llvm常见问题 (FAQ)
llvm常見問題 (FAQ)
License
可以修改 LLVM 源代碼并重新分發修改后的源代碼嗎?
可以修改 LLVM 源代碼并重新分發基于二進制文件或其它工具,而無需重新分發源代碼嗎?
源代碼
LLVM 是用什么語言編寫的?
LLVM 源代碼的可移植性如何?
使用什么 API,將值存儲到 LLVM IR 的 SSA 表示中的一個虛擬寄存器?
源語言
支持哪些源語言?
想編寫一個自托管的 LLVM 編譯器。應該如何與 LLVM 中端優化器和后端代碼生成器交互?
對用于構建編譯器的更高級別的源語言結構有什么支持?
不明白GetElementPtr指令。幫助!
使用 C 和 C++ 前端
可以將 C 或 C++ 代碼編譯為獨立于平臺的 LLVM bitcode位碼嗎?
關于demo頁面生成代碼的問題
當#include 發生的事情?llvm.global_ctors以及_GLOBAL__I_a…是什么 ?
出現在代碼中的這個“undef ”是什么?
“Go“代碼在哪里使用?
為什么 instcombine +simplifiedcfg對不匹配的函數的調用變成“無法訪問”?為什么不讓驗證者拒絕?
許可證license
可以修改 LLVM 源代碼,重新分發修改后的源代碼嗎?
是的。修改后的源代碼分發必須保留版權聲明并遵循Apache License v2.0 with LLVM Exceptions 中列出的條件。
可以修改 LLVM 源代碼,重新分發二進制文件或工具,而無需重新分發源代碼嗎?
是的。在比 GPL 限制更少的許可下分發 LLVM,如上面的第一個問題所述。
源代碼
LLVM 是用什么語言編寫的?
所有 LLVM 工具和庫都是用 C++ 編寫的,并廣泛使用了 STL。
LLVM 源代碼的可移植性如何?
LLVM 源代碼應該可以移植到大多數modern Unix-like操作系統。LLVM 對 Windows 系統也有很好的支持。大多數代碼是用標準 C++ 編寫的,操作系統服務抽象支持庫。構建和測試 LLVM 所需的工具已移植到大平臺。
使用什么 API,將值存儲到 LLVM IR 的 SSA 表示中的一個虛擬寄存器?
簡而言之:不能。一旦理解了正在發生的事情,這實際上是一個愚蠢的問題。基本上,在代碼中:
%result = add i32 %foo, %bar
%result只是給予一個add 指令Value。換句話說,%result 就是添加指令。“賦值”沒有明確地將任何東西“存儲”到任何“虛擬寄存器”;“ =”更像是數學意義上的相等。
詳細解釋:為了生成 IR 的文本表示,必須為每條指令指定某種名稱,以便其它指令可以在文本上引用。但是,從 C++ 操作的同構內存中表示沒有這樣的限制,指令可以簡單地保留指向Value引用的任何指針。事實上,虛擬編號臨時變量的名稱,如%1,根本沒有在內存中明確表示(參見資料 Value::getName())。
源語言
支持哪些源語言?
LLVM 目前通過Clang完全支持 C 和 C++ 源語言。許多其它語言前端是使用 LLVM 編寫的,在使用 LLVM 的項目中提供了一個不完整的列表 。
編寫一個自托管的 LLVM 編譯器。應該如何與 LLVM 中端優化器和后端代碼生成器交互?
編譯器前端將通過以 LLVM 中間表示 (IR) 格式,創建模塊來與 LLVM 通信。用語言(不是 C++)編寫編譯器,有 3 種主要方法可以從前端生成 LLVM IR:
使用語言的 FFI(外部函數接口)調用 LLVM 庫代碼。
for:最佳跟蹤對 LLVM IR、.ll 語法和 .bc 格式的更改
for:啟用運行 LLVM 優化過程,無需發出/解析開銷
for:很好地適應 JIT 上下文
against:要寫很多丑陋的膠水代碼
從編譯器的本地語言發出 LLVM 匯編程序集。
for:非常容易上手
against:當連接到中間端時,.ll 解析器比bitcode位碼讀取器慢
against:跟蹤 IR 的變化可能更難
從編譯器的本地語言發出 LLVM 位碼。
for:在與中端接口時,可以使用更高效的bitcode位碼reader
against:必須用語言重新設計 LLVM IR 對象模型和位碼編寫器
against:跟蹤 IR 的變化可能更難
如果選擇第一個選項,include/llvm-c 中的 C 綁定應該會有很大幫助,因為大多數語言都強烈支持與 C 的接口。從托管代碼調用 C 的最常見障礙,與垃圾收集器的接口。C 接口設計只需要很少的內存管理,在這方面很簡單。
對用于構建編譯器的更高級別的源語言結構,有什么支持?
目前,沒有太多。LLVM 支持對代碼有用的中間表示,但不支持大多數編譯器所需的高級(抽象語法樹)表示。沒有用于詞法或語義分析的工具。
不明白GetElementPtr指令。幫助!
請參閱 The Often Misunderstood GEP Instruction。
使用 C 和 C++ 前端
可以將 C 或 C++ 代碼編譯為獨立于平臺的 LLVM 位碼嗎?
不,C 和 C++ 本質上是依賴于平臺的語言。最明顯的例子是預處理器。使 C 代碼具有可移植性的非常常見方法,使用預處理器來包含特定于平臺的代碼。實際上,預處理后,平臺的信息會丟失,結果本質上取決于預處理所針對的平臺。
另一個例子是sizeof。sizeof(long)在平臺之間變化是很常見的。在大多數 C 前端,sizeof立即擴展為常量,從而硬連接特定于平臺的細節。
此外,由于許多平臺根據 C 定義它們的 ABI,并且由于 LLVM 比 C 級別低,因此前端當前必須發出platform-specific的 IR,以使結果符合平臺 ABI。
關于demo頁面生成代碼的問題
當#include 發生的事情,llvm.global_ctors以及_GLOBAL__I_a…是什么?
如果#include的header換成C ++翻譯單元,該文件可能會使用std::cin/ std::cout/ …全局對象。但是,C++ 不保證不同翻譯單元中靜態對象之間的初始化順序,靜態std::cout,例如,如果使用了 .cpp 文件中的靜態 ctor/dtor ,該對象不一定會在使用自動初始化。
為了讓std::cout和friends 在這些場景中正常工作,使用的 STL 聲明了一個靜態對象,該對象在包含。該對象具有靜態構造函數和析構函數,用于在全局 iostream 對象,可能在文件中使用之前初始化和銷毀。在.ll文件中看到的代碼,對應于構造函數和析構函數的注冊代碼。
如果希望更容易理解演示頁面中編譯器生成的 LLVM 代碼,請參考使用printf()代替 iostreams 來打印信息。
所有的代碼都去哪兒了?
如果正在使用 LLVM 演示頁面,了解輸入的所有代碼發生了什么。請記住,演示腳本是通過 LLVM 優化器運行代碼的,如果代碼實際上沒有做任何有用的事情,可能會全部刪除。
為了防止這種情況,確保確實需要該代碼。例如,如果正在計算某個表達式,則從函數返回值,而不是將其留在局部變量中。如果真的想約束優化器,可以讀取和分配volatile全局變量。
出現在代碼中的這個“undef ”是什么?
undef是表示未定義值的 LLVM 方式。如果在使用變量之前未初始化變量,則可以獲得這些。例如,C 函數:
int X() { int i; return i; }
編譯為“ret i32 undef”,因為“i ”從未為其指定值。
為什么 instcombine +simplifiedcfg 將對調用約定不匹配的函數的調用變成“無法訪問”?為什么不讓驗證者拒絕?
這是使用自定義調用約定的前端作者遇到的一個常見問題:
需要確保在函數和每次調用函數時,都設置正確的調用約定。例如,這段代碼:
define fastcc void @foo() {
ret void
}
define void @bar() {
call void @foo()
ret void
}
優化為:
define fastcc void @foo() {
ret void
}
define void @bar() {
unreachable
}
……用“opt -instcombine -simplifycfg”。這通常會bites people難受,因為“他們所有的代碼都消失了”。間接調用需要在調用者和被調用者上設置調用約定,所以人們經常問為什么不讓驗證者拒絕這種事情。
答案是這段代碼有未定義的行為,但并不違法。如果將其設為非法,那么每個可能會創建的轉換都必須確保不會發生,并且存在可以創建此類構造的有效代碼(在死代碼中)。可能導致這種情況發生的事情是相當contrived的,但仍然需要接受。下面是一個例子:
define fastcc void @foo() {
ret void
}
define internal void @bar(void()* %FP, i1 %cond) {
br i1 %cond, label %T, label %F
T:
call void %FP()
ret void
F:
call fastcc void %FP()
ret void
}
define void @test() {
%X = or i1 false, false
call void @bar(void()* @foo, i1 %X)
ret void
}
在這個例子中,“test”總是傳遞@foo/ falseinto bar,確保conv 正確的動態調用(因此,代碼定義得很好)。如果通過內聯程序運行,會得到這個(明確的“或”是存在的,這樣內聯程序就不會死代碼,消除一堆東西):
define fastcc void @foo() {
ret void
}
define void @test() {
%X = or i1 false, false
br i1 %X, label %T.i, label %F.i
T.i:
call void @foo()
br label %bar.exit
F.i:
call fastcc void @foo()
br label %bar.exit
bar.exit:
ret void
}
可以看到內聯傳遞@foo,使用錯誤的調用約定進行了未定義的調用。真的不想讓內聯程序知道這種事情,需要是有效的代碼。在這種情況下,死代碼消除可以輕松刪除未定義的代碼。但是,如果%X是 @test輸入參數,內聯程序將生成以下內容:
define fastcc void @foo() {
ret void
}
define void @test(i1 %X) {
br i1 %X, label %T.i, label %F.i
T.i:
call void @foo()
br label %bar.exit
F.i:
call fastcc void @foo()
br label %bar.exit
bar.exit:
ret void
}
這一點的有趣之處在于,對于定義良好的代碼來說,%X 必須為 false,但沒有多少死代碼能夠刪除無法訪問的損壞調用。由于 instcombine/simplifycfg將 undefined 調用變為 unreachable,最終得到了一個條件為 unreachable 的分支:
unreachable 的分支永遠不會發生,所以“-inline -instcombine -simplifycfg”能夠產生:
define fastcc void @foo() {
ret void
}
define void @test(i1 %X) {
F.i:
call fastcc void @foo()
ret void
}
參考鏈接:Frequently Asked Questions (FAQ
總結
以上是生活随笔為你收集整理的llvm常见问题 (FAQ)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QNX Hypervisor管理程序
- 下一篇: ResNet50结构