LLVM一些語法規則
LLVM文檔
LLVM編譯器基礎架構支持廣泛的項目,從工業強度編譯器到專門的JIT應用程序,再到小型研究項目。
同樣,文檔分為幾個針對不同受眾的高級別分組:
LLVM設計概述
幾篇介紹性論文和演講。
- LLVM編譯器簡介: 向用戶介紹LLVM的演示文稿
- LLVM簡介: 本章提供了面向編譯器黑客的LLVM介紹
- LLVM:終身計划分析與轉型的編制框架:設計概述
- LLVM:多階段優化的基礎架構:更多細節
用戶指南
這些內容適用於那些剛剛接觸LLVM系統的人。
注意:如果是僅對使用基於LLVM的編譯器感興趣的用戶,則應該查看Clang。此處的文檔適用於需要使用中間LLVM表示的用戶。
- LLVM系統入門 : 討論如何使用LLVM基礎架構快速啟動和運行。從解包和編譯分發到執行某些工具的一切。
- 使用CMake構建LLVM: 使用CMake構建系統的主要入門指南的附錄。
- 在ARM平台上構建LLVM指南: 關於在ARM上構建和測試LLVM/Clang的注意事項。
- 如何使用配置文件引導優化構建Clang和LLVM: 使用PGO構建LLVM/Clang的注意事項。
- 如何為ARM平台交叉編譯compiler-rt Builtins: 關於交叉構建和測試ARM的編譯器-rt內置函數的注意事項。
- 如何使用Clang/LLVM交叉編譯Clang/LLVM:關於交叉構建和測試LLVM / Clang的注意事項。
- 使用Microsoft Visual Studio開始使用LLVM系統:Windows上使用Visual Studio的主要入門指南的附錄。
- LLVM命令指南: LLVM命令行實用程序的參考手冊(LLVM工具的“man”頁面)
- LLVM的分析和轉換Passes:LLVM中實現的優化和分析列表
- LLVM FAQ: 常見問題和問題列表及其解決方案。
- 當前版本的發布說明:這描述了新功能,已知錯誤和其它限制。
- 如何提交LLVM錯誤報告: 有關正確提交有關您在LLVM系統中遇到的任何錯誤的信息的說明。
- sphinx模板快速入門:使用LLVM測試基礎結構的參考手冊。
- LLVM測試套件基礎結構指南:使用LLVM測試基礎結構的參考手冊。
- LLVM測試套件使用指南:描述如何編譯和運行測試套件基准測試。
- 如何構建C,C ++,ObjC和ObjC ++前端:從源代碼構建clang前端的說明。
- LLVM詞典:LLVM中使用的首字母縮略詞,術語和概念的定義。
- 如何將構建配置添加到LLVM Buildbot基礎結構:有關將新構建器添加到LLVM buildbot master的說明。
- YAML I/O:使用LLVM的YAML I/O庫的參考指南。
- 經常被誤解的GEP指令:關於LLVM最常被誤解的指令的一些常見問題的答案。
- 前端作者的性能提示:前端作者關於如何生成IR的技巧的集合,LLVM能夠有效地優化。
- Dockerfiles用於構建LLVM的指南:使用隨LLVM提供的Dockerfiles的參考。
編程文檔
對於使用LLVM作為庫的應用程序的開發人員。
- LLVM語言參考手冊:定義LLVM中間表示和不同節點的匯編表單。
- LLVM原子指令和並發指南:有關LLVM的並發模型的信息。
- LLVM程序員手冊:介紹LLVM源代碼庫的總體布局,重要的類和API,以及一些提示和技巧。
- LLVM擴展:LLVM特定的工具和格式擴展LLVM尋求兼容性。
- CommandLine 2.0庫手冊:提供有關使用命令行解析庫的信息。
- LLVM編碼標准:詳細介紹了LLVM編碼標准,並提供了有關編寫高效C ++代碼的有用信息。
- 如何為類層次結構設置LLVM樣式的RTTI:如何讓
isa<>
,dyn_cast<>
等可供類層次的客戶。 - 擴展LLVM:添加指令,內在函數,類型等:在這里查看如何向LLVM添加指令和內在函數。
- libFuzzer - 用於覆蓋引導的模糊測試的庫:用於編寫進程中引導模糊器的庫
- 模糊LLVM庫和工具:有關編寫和使用Fuzzers查找LLVM中的錯誤的信息.
- Scudo硬化分配器:一個實現安全加固的malloc()的庫。
- 使用-opt-bisect-limit調試優化錯誤:用於調試優化引發的故障的命令行選項。
子系統文檔
適用於API客戶端和LLVM開發人員。
- 編寫LLVM Passes:有關如何編寫LLVM轉換和分析的信息
- 編寫LLVM后端:有關如何為機器目標編寫LLVM后端的信息
- LLVM與目標無關的代碼生成器:LLVM代碼生成器的設計和實現。如果正在將LLVM重新定位到新架構,設計新的codegen傳遞或增強現有組件,則非常有用。
- 機器IR(MIR)格式參考手冊:MIR序列化格式的參考手冊,用於測試LLVM的代碼生成過程。
- TableGen:描述了TableGen工具,LLVM代碼生成器大量使用它。
- LLVM別名分析基礎結構:有關如何編寫新別名分析實現,或如何使用現有分析的信息。
- MemorySSA:有關LLVM中的MemorySSA實用程序的信息,以及如何使用。
- 使用LLVM進行垃圾收集:接口源語言編譯器應該用於編譯GC程序。
- 使用LLVM進行源級別調試:本文檔描述了LLVM源代碼級調試器背后的設計和理念。
- LLVM中的自動矢量化:本文檔描述了LLVM中矢量化的當前狀態
- LLVM中的異常處理:本文檔描述了LLVM中異常處理的設計和實現
- 如何添加一個受約束的浮點內在函數:在LLVM中添加新的約束數學內在時,提供必要的步驟。
- LLVM bugpoint工具:設計和使用:自動錯誤查找器和測試用例減少器描述和使用信息
- LLVM Bitcode文件格式:這描述了用於LLVM“bc”文件的文件格式和編碼。
- 支持庫:本文檔描述了LLVM支持庫(lib/Support)以及如何使LLVM源代碼可移植
- LLVM鏈接時間優化:設計和實現:本文檔描述了LLVM模塊間優化器與鏈接器及其設計之間的接口
- LLVM黃金插件:如何在Linux上使用鏈接時,優化來構建程序。
- 使用GDB調試JIT-ed代碼:如何使用GDB調試JITed代碼。
- MCJIT設計與實施:描述了MCJIT執行引擎的內部工作原理
- LLVM分支權重元數據:提供有關分支預測信息的信息。
- LLVM塊頻率術語:提供有關BlockFrequencyInfo 分析過程中使用的術語的信息
- LLVM中的分段堆棧:本文檔描述了分段堆棧以及它們在LLVM中的使用方式
- LLVM的可選豐富的反匯編輸出:本文檔介紹了可選的豐富反匯編輸出語法
- 如何使用屬性:回答有關新屬性基礎結構的一些問題。
- NVPTX后端用戶指南:本文檔描述了使用NVPTX后端編譯GPU內核。
- AMDGPU后端用戶指南:本文檔描述了使用AMDGPU后端編譯GPU內核。
- LLVM中的堆棧映射和補丁點:LLVM支持將指令地址,映射到值的位置並允許修補代碼。
- 在big endian模式下使用ARM NEON指令:LLVM支持在大端ARM目標上,生成NEON指令有點不直觀。本文檔解釋了實施和理由。
- LLVM代碼覆蓋映射格式:LLVM代碼覆蓋映射格式
- LLVM中的垃圾收集安全點:這描述了一組垃圾收集支持的實驗擴展。
- MergeFunctions Pass,它是如何工作的:描述合並優化的函數。
- InAlloca屬性的設計和使用:inalloca參數屬性的描述。
- FaultMaps和隱式檢查:LLVM支持折疊控制流入錯誤機器指令。
- 用clang編譯CUDA:LLVM對CUDA的支持。
- LLVM中的協同程序:LLVM中的協同程序.
- 全局指令選擇:這描述了原型指令選擇替換GlobalISel
- XRay儀表:有關如何在LLVM中使用XRay的高級文檔。
- 使用XRay進行調試:如何使用XRay調試應用程序的示例。
- Microsoft PDB文件格式:Microsoft PDB(程序數據庫)文件格式的詳細說明。
- 控制流程驗證工具設計文檔:控制流完整性驗證工具的說明
- 投機負荷強化:Spectre v1的推測負載強化緩解的描述
- 堆棧安全分析:本文檔描述了局部變量的堆棧安全性分析的設計。
LLVM IR 鏈接類型
在LLVM程序中,所有全局變量和函數都具有以下類型的鏈接之一:
private
“private”鏈接的全局值,只能由當前模塊中的對象直接訪問。特別地,鏈接代碼到一個包含”private”全局值的模塊時,在必要情況下,可能會造成”private”全局值rename來避免沖突。因為這個symbol是對當前模塊私有的,所以,所有對這個全局值的引用,都可以被更新(名字)。這並不會在object file的任何symbol table中展示出來。
internal
與”private”相似,但該值在object file表現為local symbol (STB_LOCAL in the case of ELF) . 這對應於C中的“static”的關鍵字的概念。
available_externally
帶有available_externally
鏈接標識的全局變量,不會存放到當前object file相應的LLVM模塊。存在是為了內聯和其它優化行為的發生,提供當前模塊的一份全局定義,這份定義是從一個外部模塊中獲得的。帶有available_externally
鏈接標志的全局變量,允許在任意時刻丟棄,而其它方面與”linkonce_odr”相似。這個鏈接類型,只允許在定義中使用,不允許在聲明中使用。
linkonce
帶有linkonce
鏈接標識的全局變量,在鏈接過程中與其它同名的全局變量合並。這可以被用於實現內聯函數,模板,或其它必須在每一個編譯單元內使用的代碼的形式,但主體可以被一個更詳細的定義覆蓋。
全局用linkonce
聯動,合並具有相同名稱的其它全局聯動時發生。這可以被用來實現某些形式的內聯函數,模板,或者必須在使用每個轉換單元中生成的,但其中的主體,可以具有更明確的定義,以后被覆蓋其它代碼。
weak
除了未被引用的帶”weak”鏈接標識的全局變量,可能會被拋棄外,“weak”鏈接標識,擁有與”linkonce”相同的合並語義。這個標志被使用於在C源代碼中,聲明為”weak”的全局變量。
common
“common” 鏈接標識與“weak”鏈接標識很相似,但“common”鏈接標識被使用於C中的tentative definition,例如 int X;
在全局作用域。帶有“common”鏈接標識的符號,以一種與weak symbols相同的方式被合並,但這些符號,即使未被引用也不會被刪除。 common 符號可能不會有一個明確的section,必須被0值初始化(根據ELF鏈接規則,0值初始化的符號只通過“.bss section”提供長度占位,但不在文件中占有位置),不可能被標志為constant。Functions和aliases 不可以帶有“common” 鏈接標識。(因為Functions和aliases,不可能被tentative definition)。
appending
“appending”鏈接標識,只能用於數組類型的指針全局變量。但兩個帶有“appending”鏈接標識的全局變量被鏈接到一起,這兩個全局數組,追加合並到一起。 may This is the LLVM, typesafe, equivalent of having the system linker append together “sections” with identical names when .o files are linked.
不幸的是,這與.o文件中的任何功能都不對應,只能用於llvm.global_ctors
llvm特別解釋的變量。
extern_weak
這個鏈接標識的語義,遵循ELF object file模型:除非被鏈接,否則帶有extern_weak的symbol是弱的,如果沒有被鏈接,該符號會變為null,而不是作為未定義引用。
linkonce_odr, weak_odr
某些語言允許不同的全局變量被合並,例如具有不同的語義的兩個函數。其它語言,如C++ ,確保只等效的全局變量才可以合並( “one definition rule” – “ ODR ”)。這些語言可以使用linkonce_odr和weak_odr鏈接標識,表明全局變量將只與等效的全局變量合並。這些鏈接標識類型的其它語義,與其非ODR版本相同。
external
如果上述標識符都沒被使用,該全局變量的是外部可見的,意味着它參與鏈接,可用於解析外部符號引用。
一個函數聲明擁有除external
或 extern_weak
以外的鏈接標識是不合法的。
LLVM IR 模塊結構
LLVM程序由Module’s組成,每個程序模塊都是輸入程序的翻譯單元。每個模塊由函數,全局變量和符號表條目組成。模塊可以與LLVM鏈接器組合在一起,LLVM鏈接器合並函數(和全局變量)定義,解析前向聲明,合並符號表條目。
以下是“hello world”模塊的示例:
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8*...
%cast210 = getelementptr [13 x i8], [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!0 = !{i32 42, null, !"string"}
!foo = !{!0}
這個例子由一個全局變量”.str”,一個”puts”函數的外部聲明,一個”main”函數的函數定義和一個具名元數據”foo”組成。
LLVM IR 標識符
LLVM IR 標識符有兩種基本類型:
- 全局標識符(函數,全局變量)以’@’字符開頭。
- 本地標識符(寄存器名稱,類型)以’%’字符開頭。
標識符有三種不同的格式,分別用於不同的目的:
- 命名值表示為帶有前綴的字符串。例如%foo,@DivisionByZero,%a.really.long.identifier。使用的實際正則表達式是’
[%@][-a-zA-Z$._][-a-zA-Z$._0-9]*’。名稱中需要其它字符的標識符,可以用引號括起來。可以使用十六進制字符的ASCII代碼”\xx”,轉義特殊字符xx。通過這種方式,任何字符都可以在名稱值中使用,甚至可以引用自己。該”\01″前綴可以在全局值可用於壓制截斷。 - 未命名的值表示為帶有前綴的無符號數值。例如%12,@2,%44。
- 常量,將在后面的關於常量的部分進行描述。
LLVM要求值以一個前綴開始有兩個原因:
- 編譯器不需要擔心值的名稱會和保留字(reserved words)沖突,保留字(reserved word)集,可以在未來擴展的時候不會出現懲罰(penalty,不會發生沖突)。
- 非命名的identifier,允許編譯器快速找出一個臨時變量,不會造成符號表沖突。
LLVM中的保留字與其它語言的保留字非常相似。有相應的關鍵字對應着不同的操作碼,有(add, bitcast, ret, etc…),原始類型名(void, i32,etc…)和其它。這些保留字不會與變量名沖突,沒有一個是以前綴(% or @)開頭的。
這里有一個表示用8乘上一個整型變量%x的LLVM代碼例子:
#The easy way:
%result = mul i32 %X, 8
#在強度折減后:
%result = shl i32 %X, 3
#And the hard way:
%0 = add i32 %X, %X ; yields {i32}:%0
%1 = add i32 %0, %0 ; yields {i32}:%1
%result = add i32 %1, %1
用8乘上%x的最后的方式,說明了LLVM的幾個重要的詞法特點:
- 注解是以 ; 分隔,直到當前行的結尾
- 當計算的結果不能被賦值的一個具名值的時候,非命名臨時變量被創建
- 非具名臨時變量,按順序編號的(使用一個遞增計數器,從0開始)。注意整個基本塊,都被包含在這種編號方法中。例如,如果一個基本塊的入口,沒有被給予一個標簽名,那么,就會獲得一個編號0。
也表明了一個在這個文檔,應該遵循的約定。當演示指令的時候,應該使一個定義了被創建的值的類型和名稱的注釋,緊跟這條指令后面。
LLVM語言參考手冊
LLVM是一個基於SSA(靜態單賦值)表示,提供了類型安全,低級別操作,靈活性和表現“所有”高級語言的能力。他是在LLVM編譯策略的各個階段中使用的通用代碼表示。
LLVM的代碼表示形式被設計為使用三種不同的格式:
- 表示為在內存中編譯器中間語言;
- 表示為在磁盤上的位碼(適合於即時編譯器的快速加載);
- 表示為人類可讀的匯編語言。
LLVM為編譯器的高效轉換和分析,提供了強大的中間語言,同時提供一個自然的方法來調試和可視化的轉換。LLVM的這三種不同形式的代碼表示的,都是等價的。本文檔描述了人類可讀的代表性和符號。
LLVM IR的目標是實現輕量和低級別同時是有表現力的,類型化,可擴展。目標是成為一個“通用IR”的序列,在一個足夠低的水平是高層次的思想,可以被清晰地映射到(類似於微處理器是如何“萬能的IR”,這讓很多源語言被映射到) 。通過提供類型信息,LLVM可以作為優化的目的:例如,通過指針分析可以證明,一個C的自動變量,從來沒有被當前函數以外的地方訪問,就可以被提升到一個簡單的SSA值中,而不是存儲單元中。
注意這個文檔描述規范化的LLVM匯編語言。這有區別於“可以被解釋的就是(well-formed)格式良好的”的概念。例如,下面的指令在語法上是OK的,但不是’well form’(格式良好的)的:
%x = add i32 1, %x
這是因為%x的定義不能支配到所有使用了%x的地方。
LLVM的架構,提供了一個驗證過程,用於驗證一個LLVM模塊是否規范化。這個pass,將自動用解釋器解釋,輸入匯編之后和優化程序輸出bitcode之前。
驗證程序驗證過程中,指出的違規暗示轉換過程中的錯誤,或輸入到解析器中的錯誤。
參考鏈接:
https://llvm.liuxfe.com/docs/
http://llvm.org/docs/LangRef.htm