LLVM Bitcode 檔案格式¶
摘要¶
本文檔描述 LLVM 位元流檔案格式以及將 LLVM IR 編碼到其中的方式。
概觀¶
通常被稱為 LLVM bitcode 檔案格式(有時也過時地稱為 bytecode)實際上是兩件事:位元流容器格式 和將 LLVM IR 編碼到容器格式中。
位元流格式是結構化資料的抽象編碼,在某些方面非常類似於 XML。與 XML 類似,位元流檔案包含標籤和巢狀結構,您可以解析檔案而無需理解標籤。與 XML 不同,位元流格式是一種二進制編碼,並且與 XML 不同,它提供了一種機制,讓檔案可以自我描述「縮寫」,這實際上是對內容的大小優化。
LLVM IR 檔案可以選擇性地嵌入到 包裝器 結構中,或嵌入到 原生物件檔案 中。這兩種機制都使得將額外資料與 LLVM IR 檔案一起嵌入變得容易。
本文檔首先描述 LLVM 位元流格式,然後描述包裝器格式,最後描述 LLVM IR 檔案使用的記錄結構。
位元流格式¶
位元流格式實際上是一個位元串流,具有非常簡單的結構。此結構包含以下概念:
請注意,llvm-bcanalyzer 工具可用於轉儲和檢查任意位元流,這對於理解編碼非常有用。
魔術數字¶
位元流的前四個位元組用作應用程式特定的魔術數字。通用 bitcode 工具可能會查看前四個位元組,以確定串流是否為已知的串流類型。但是,這些工具不應僅根據其魔術數字來判斷位元流是否有效。新的應用程式特定位元流格式一直在開發中;工具不應僅僅因為它們具有迄今未見的魔術數字而拒絕它們。
基本元素¶
位元流實際上由位元串流組成,這些位元從每個位元組的最低有效位開始依序讀取。串流由許多基本值組成,這些基本值編碼了無號整數值串流。這些整數以兩種方式編碼:一種是 固定寬度整數,另一種是 可變寬度整數。
固定寬度整數¶
固定寬度整數值將其低位直接發送到檔案。例如,3 位元整數值將 1 編碼為 001。當欄位具有已知的選項數量時,會使用固定寬度整數。例如,布林值通常使用 1 位元寬的整數進行編碼。
可變寬度整數¶
可變寬度整數 (VBR) 值編碼任意大小的值,並針對值較小的情況進行優化。給定一個 4 位元 VBR 欄位,任何 3 位元值(0 到 7)都會直接編碼,並將高位設定為零。大於 N-1 位元的值以一系列 N-1 位元區塊發出其位元,其中除了最後一個區塊外,所有區塊都設定了高位。
例如,當作為 vbr4 值發出時,值 30 (0x1E) 編碼為 62 (0b0011’1110)。從最低有效位開始的第一組四個位元指示值 6 (110) 和一個連續片段(由高位 1 指示)。下一組四個位元指示值 24 (011 << 3),沒有連續。總和 (6+24) 產生值 30。
6 位元字元¶
6 位元字元將常用字元編碼為固定 6 位元欄位。它們使用以下 6 位元值表示以下字元:
'a' .. 'z' --- 0 .. 25
'A' .. 'Z' --- 26 .. 51
'0' .. '9' --- 52 .. 61
'.' --- 62
'_' --- 63
此編碼僅適用於編碼僅由上述字元組成的字元和字串。它完全無法編碼不在集合中的字元。
字組對齊¶
有時,發出零位元直到位元流是 32 位元的倍數會很有用。這確保了串流中的位元位置可以表示為 32 位元字組的倍數。
縮寫 ID¶
位元流是 區塊 和 資料記錄 的循序序列。它們都以縮寫 ID 開頭,該 ID 編碼為固定位元寬度欄位。寬度由目前的區塊指定,如下所述。縮寫 ID 的值指定內建 ID(具有特殊含義,如下定義)或串流本身為目前區塊定義的縮寫 ID 之一。
內建縮寫 ID 的集合為:
0 - END_BLOCK — 此縮寫 ID 標記目前區塊的結尾。
1 - ENTER_SUBBLOCK — 此縮寫 ID 標記新區塊的開頭。
2 - DEFINE_ABBREV — 這定義了一個新的縮寫。
3 - UNABBREV_RECORD — 此 ID 指定未縮寫記錄的定義。
縮寫 ID 4 及以上由串流本身定義,並指定 縮寫記錄編碼。
區塊¶
位元流中的區塊表示串流的巢狀區域,並由內容特定的 ID 號碼識別(例如,LLVM IR 使用 ID 12 來表示函數主體)。區塊 ID 0-7 保留給 標準區塊,其含義由 Bitcode 定義;區塊 ID 8 及以上是應用程式特定的。巢狀區塊捕獲編碼在其中的資料的階層結構,並且在解析檔案時,各種屬性與區塊相關聯。區塊定義允許讀取器在恆定時間內有效率地跳過區塊(如果讀取器想要區塊的摘要,或者它想要有效率地跳過它不理解的資料)。LLVM IR 讀取器使用此機制來跳過函數主體,並根據需要延遲讀取它們。
在讀取和編碼串流時,會為區塊維護多個屬性。特別是,每個區塊都維護:
目前的縮寫 ID 寬度。此值在串流開始時為 2,並且每次輸入區塊記錄時都會設定。區塊條目指定區塊主體的縮寫 ID 寬度。
一組縮寫。縮寫可以在區塊內定義,在這種情況下,它們僅在該區塊中定義(子區塊和封閉區塊都看不到縮寫)。縮寫也可以在 BLOCKINFO 區塊內定義,在這種情況下,它們在與
BLOCKINFO
區塊描述的 ID 相符的所有區塊中定義。
當輸入子區塊時,會儲存這些屬性,並且新的子區塊具有自己的縮寫集和自己的縮寫 ID 寬度。當彈出子區塊時,會還原儲存的值。
ENTER_SUBBLOCK 編碼¶
[ENTER_SUBBLOCK, blockidvbr8, newabbrevlenvbr4, <align32bits>, blocklen_32]
ENTER_SUBBLOCK
縮寫 ID 指定新區塊記錄的開始。blockid
值編碼為 8 位元 VBR 識別碼,並指示要輸入的區塊類型,它可以是 標準區塊 或應用程式特定的區塊。newabbrevlen
值是 4 位元 VBR,它指定子區塊的縮寫 ID 寬度。blocklen
值是 32 位元對齊的值,它指定子區塊的大小(以 32 位元字組為單位)。此值允許讀取器一步跳過整個區塊。
END_BLOCK 編碼¶
[END_BLOCK, <align32bits>]
END_BLOCK
縮寫 ID 指定目前區塊記錄的結尾。它的結尾與 32 位元對齊,以確保區塊的大小是 32 位元的偶數倍。
資料記錄¶
資料記錄由記錄代碼和若干個(最多)64 位元整數值組成。代碼和值的解釋是應用程式特定的,並且可能因不同的區塊類型而異。記錄可以使用未縮寫記錄或縮寫進行編碼。例如,在 LLVM IR 格式中,有一個記錄編碼模組的目標三元組。代碼是 MODULE_CODE_TRIPLE
,記錄的值是字串中字元的 ASCII 碼。
UNABBREV_RECORD 編碼¶
[UNABBREV_RECORD, codevbr6, numopsvbr6, op0vbr6, op1vbr6, …]
UNABBREV_RECORD
提供預設的回退編碼,它既完全通用又極其低效。它可以透過將代碼和運算元作為 VBR 發出來描述任意記錄。
例如,將 LLVM IR 目標三元組作為未縮寫記錄發出需要發出 UNABBREV_RECORD
縮寫 ID、MODULE_CODE_TRIPLE
代碼的 vbr6、字串長度的 vbr6(等於運算元的數量)以及每個字元的 vbr6。由於沒有值小於 32 的字母,因此每個字母都需要作為至少由兩部分組成的 VBR 發出,這意味著每個字母至少需要 12 位元。這不是有效率的編碼,但它是完全通用的。
縮寫記錄編碼¶
[<abbrevid>, fields...]
縮寫記錄是縮寫 ID,後跟一組根據 縮寫定義 編碼的欄位。這使得記錄可以比使用 UNABBREV_RECORD 類型編碼的記錄更密集地編碼,並允許在串流本身中指定縮寫類型,這使得檔案可以完全自我描述。縮寫的實際編碼在下面定義。
記錄代碼是縮寫記錄的第一個欄位,可以在縮寫定義中編碼(作為文字運算元)或在縮寫記錄中提供(作為固定或 VBR 運算元值)。
縮寫¶
縮寫是位元流的重要壓縮形式。其想法是為一類記錄指定一次密集編碼,然後使用該編碼發出許多記錄。將編碼發送到檔案需要空間,但當發出使用它的記錄時,空間會被收回(希望還多一些)。
縮寫可以根據每個客戶端、每個檔案動態確定。由於縮寫儲存在位元流本身中,因此相同格式的不同串流可以根據特定串流的需求包含不同的縮寫集。作為一個具體範例,LLVM IR 檔案通常為二元運算子發出縮寫。如果特定的 LLVM 模組不包含或很少包含二元運算子,則不需要發出縮寫。
DEFINE_ABBREV 編碼¶
[DEFINE_ABBREV, numabbrevopsvbr5, abbrevop0, abbrevop1, …]
DEFINE_ABBREV
記錄將縮寫添加到此區塊範圍內目前定義的縮寫列表中。此定義僅存在於此直接區塊內 — 它在子區塊或封閉區塊中不可見。縮寫會隱式地依序分配 ID,從 4 開始(第一個應用程式定義的縮寫 ID)。在 BLOCKINFO
記錄中為特定區塊類型定義的任何縮寫首先按順序接收 ID,然後是區塊本身內定義的任何縮寫。縮寫資料記錄引用此 ID 以指示它們正在調用哪個縮寫。
縮寫定義由 DEFINE_ABBREV
縮寫 ID 組成,後跟一個 VBR,用於指定縮寫運算元的數量,然後是縮寫運算元本身。縮寫運算元有三種形式。它們都以單個位元開頭,指示縮寫運算元是文字運算元(當位元為 1 時)還是編碼運算元(當位元為 0 時)。
文字運算元 — [11, litvaluevbr8] — 文字運算元指定結果中的值始終是單個特定值。此特定值在指示它是文字運算元的位元之後作為 vbr8 發出。
沒有資料的編碼資訊 — [01, encoding3] — 沒有額外資料的運算元編碼僅作為其代碼發出。
具有資料的編碼資訊 — [01, encoding3, valuevbr5] — 具有額外資料的運算元編碼作為其代碼發出,後跟額外資料。
可能的運算元編碼為:
固定 (代碼 1):欄位應作為 固定寬度值 發出,其寬度由運算元的額外資料指定。
VBR (代碼 2):欄位應作為 可變寬度值 發出,其寬度由運算元的額外資料指定。
陣列 (代碼 3):此欄位是值的陣列。陣列運算元沒有額外資料,但預期另一個運算元跟隨它,指示陣列的元素類型。在縮寫記錄中讀取陣列時,第一個整數是 vbr6,它指示陣列長度,後跟陣列的編碼元素。陣列只能作為縮寫的最後一個運算元出現(除了給出陣列類型的最後一個運算元之外)。
Char6 (代碼 4):此欄位應作為 char6 編碼值 發出。此運算元類型不採用額外資料。Char6 編碼通常用作陣列元素類型。
Blob (代碼 5):此欄位作為 vbr6 發出,後跟填充到 32 位元邊界(用於對齊)和 8 位元物件陣列。位元組陣列之後是尾部填充,以確保其總長度是 4 個位元組的倍數。這使得讀取器可以非常有效率地解碼資料,而無需複製它:它可以使用指向映射檔案中資料的指標並直接指向它。Blob 只能作為縮寫的最後一個運算元出現。
例如,LLVM 模組中的目標三元組編碼為 [TRIPLE, 'a', 'b', 'c', 'd']
形式的記錄。考慮一下,如果位元流發出了以下縮寫條目:
[0, Fixed, 4]
[0, Array]
[0, Char6]
當發出具有此縮寫的記錄時,上述條目將作為以下內容發出:
[4abbrevwidth, 24, 4vbr6, 06, 16, 26, 36]
這些值是:
第一個值 4 是此縮寫的縮寫 ID。
第二個值 2 是 LLVM IR 檔案
MODULE_BLOCK
區塊中TRIPLE
記錄的記錄代碼。第三個值 4 是陣列的長度。
其餘值是
"abcd"
的 char6 編碼值。
使用此縮寫,三元組僅以 37 位元發出(假設縮寫 ID 寬度為 3)。如果沒有縮寫,則需要明顯更多的空間來發出目標三元組。此外,由於 TRIPLE
值不是作為文字在縮寫中發出的,因此縮寫也可以用於任何其他字串值。
標準區塊¶
除了基本區塊結構和記錄編碼之外,位元流還定義了特定的內建區塊類型。這些區塊類型指定如何解碼串流或其他元資料。將來可能會新增新的標準區塊。區塊 ID 0-7 保留給標準區塊。
#0 - BLOCKINFO 區塊¶
BLOCKINFO
區塊允許描述其他區塊的元資料。目前指定的記錄為:
[SETBID (#1), blockid]
[DEFINE_ABBREV, ...]
[BLOCKNAME, ...name...]
[SETRECORDNAME, RecordID, ...name...]
SETBID
記錄(代碼 1)指示正在描述哪個區塊 ID。SETBID
記錄可以在整個區塊中多次出現,以變更正在描述的區塊 ID。在任何其他記錄之前,必須有一個 SETBID
記錄。
標準 DEFINE_ABBREV
記錄可以出現在 BLOCKINFO
區塊內,但與它們在普通區塊中出現不同的是,縮寫是為與我們正在描述的區塊 ID 相符的區塊定義的,而不是 BLOCKINFO
區塊本身。在 BLOCKINFO
區塊中定義的縮寫接收縮寫 ID,如 DEFINE_ABBREV 中所述。
BLOCKNAME
記錄(代碼 2)可以選擇性地在此區塊中出現。記錄的元素是區塊的字串名稱的位元組。llvm-bcanalyzer 可以使用它來以符號方式轉儲 bitcode 檔案。
SETRECORDNAME
記錄(代碼 3)也可以選擇性地在此區塊中出現。第一個運算元值是記錄 ID 號碼,記錄的其餘元素是記錄的字串名稱的位元組。llvm-bcanalyzer 可以使用它來以符號方式轉儲 bitcode 檔案。
請注意,儘管 BLOCKINFO
區塊中的資料被描述為「元資料」,但它們包含的縮寫對於從相應區塊解析記錄至關重要。跳過它們是不安全的。
Bitcode 包裝器格式¶
LLVM IR 的 Bitcode 檔案可以選擇性地包裝在簡單的包裝器結構中。此結構包含一個簡單的標頭,指示嵌入式 BC 檔案的偏移量和大小。這允許將額外資訊與 BC 檔案一起儲存。此檔案標頭的結構為:
[Magic32, Version32, Offset32, Size32, CPUType32]
每個欄位都是以小端形式儲存的 32 位元欄位(與其餘 bitcode 檔案欄位相同)。魔術數字始終為 0x0B17C0DE
,版本目前始終為 0
。Offset 欄位是以位元組為單位的偏移量,指向檔案中 bitcode 串流的開頭,Size 欄位是以位元組為單位的串流大小。CPUType 是一個目標特定的值,可用於編碼目標的 CPU。
原生物件檔案包裝器格式¶
LLVM IR 的 Bitcode 檔案也可以包裝在原生物件檔案中(即 ELF、COFF、Mach-O)。對於 MachO,bitcode 必須儲存在名為 __LLVM,__bitcode
的物件檔案區段中,對於其他物件格式,則必須儲存在名為 .llvmbc
的區段中。ELF 物件還額外支援 FatLTO 的 .llvm.lto
區段,其中包含適用於 LTO 編譯的 bitcode(即,已通過預先連結 LTO 管道的 bitcode)。.llvmbc
區段早於 LLVM 中的 FatLTO 支援,並且可能並不總是包含適用於 LTO 的 bitcode(即,來自 -fembed-bitcode
)。包裝器格式適用於在編譯管道中容納 LTO,其中中間物件必須是原生物件檔案,其中包含其他區段中的元資料。
並非所有工具都支援此格式。例如,lld 和 gold 外掛程式在連結物件檔案時將忽略 .llvmbc
區段,但在傳遞正確的命令列選項時可以使用 .llvm.lto
區段。
LLVM IR 編碼¶
LLVM IR 透過定義區塊和記錄編碼到位元流中。它將區塊用於常數池、函數、符號表等。它將記錄用於指令、全域變數描述符、類型描述等。本文檔未描述寫入器使用的縮寫集,因為這些縮寫在檔案中是完全自我描述的,並且不允許讀取器建立任何關於此的知識。
基礎¶
LLVM IR 魔術數字¶
LLVM IR 檔案的魔術數字是:
[‘B’8, ‘C’8, 0x04, 0xC4, 0xE4, 0xD4]
帶號 VBR¶
可變寬度整數 編碼是一種有效率的方式來編碼任意大小的無號值,但對於編碼帶號值來說非常低效,因為帶號值會被視為最大無號值。
因此,特定寬度的帶號 VBR 值按如下方式發出:
正值作為指定寬度的 VBR 發出,但其值向左移動一位元。
負值作為指定寬度的 VBR 發出,但取反值向左移動一位元,並設定低位元。
使用此編碼,可以有效率地發出小的正值和小負值。帶號 VBR 編碼用於 CONSTANTS_BLOCK
區塊中的 CST_CODE_INTEGER
和 CST_CODE_WIDE_INTEGER
記錄。它也用於 MODULE_CODE_VERSION 1 中的 phi 指令運算元。
LLVM IR 區塊¶
LLVM IR 定義了以下區塊
8 — MODULE_BLOCK — 這是頂層區塊,包含整個模組,並描述各種模組層級的資訊。
9 — PARAMATTR_BLOCK — 這枚舉了參數屬性。
10 — PARAMATTR_GROUP_BLOCK — 這描述了屬性群組表。
11 — CONSTANTS_BLOCK — 這描述了模組或函數的常數。
12 — FUNCTION_BLOCK — 這描述了函數主體。
14 — VALUE_SYMTAB_BLOCK — 這描述了值符號表。
15 — METADATA_BLOCK — 這描述了元數據項目。
16 — METADATA_ATTACHMENT — 這包含將元數據與函數指令值關聯的紀錄。
17 — TYPE_BLOCK — 這描述了模組中的所有類型。
23 — STRTAB_BLOCK — 位元碼檔案的字串表。
MODULE_BLOCK 內容¶
MODULE_BLOCK
區塊(id 8)是 LLVM 位元碼檔案的頂層區塊,且每個位元碼檔案中的模組都必須恰好包含一個。具有多模組位元碼的位元碼檔案是有效的。除了(如下所述)包含關於模組資訊的紀錄之外,MODULE_BLOCK
區塊可能包含以下子區塊
MODULE_CODE_VERSION 紀錄¶
[VERSION, version#]
VERSION
紀錄(代碼 1)包含一個單一值,指示格式版本。目前支援版本 0、1 和 2。版本 0 和 1 之間的差異在於每個 FUNCTION_BLOCK 中指令運算元的編碼。
在版本 0 中,由指令定義的每個值都被分配一個在函數中唯一的 ID。函數層級的值 ID 從 NumModuleValues
開始分配,因為它們與模組層級的值共享相同的命名空間。值枚舉器在每個函數之後重置。當一個值是指令的運算元時,該值 ID 用於表示該運算元。對於大型函數或大型模組,這些運算元值可能很大。
版本 1 中的編碼嘗試避免常見情況下出現大型運算元值。運算元不是直接使用值 ID,而是相對於當前指令進行編碼。因此,如果運算元是由前一個指令定義的值,則該運算元將被編碼為 1。
例如,而不是
#n = load #n-1
#n+1 = icmp eq #n, #const0
br #n+1, label #(bb1), label #(bb2)
版本 1 將指令編碼為
#n = load #1
#n+1 = icmp eq #1, (#n+1)-#const0
br #1, label #(bb1), label #(bb2)
請注意,在範例中,作為常數的運算元也使用相對編碼,而像基本區塊標籤這樣的運算元則不使用相對編碼。
前向參考將導致負值。這可能效率低下,因為運算元通常被編碼為無符號 VBR。然而,前向參考很少見,除非在 phi 指令的情況下。對於 phi 指令,運算元被編碼為 有符號 VBR 以處理前向參考。
在版本 2 中,模組紀錄 FUNCTION
、GLOBALVAR
、ALIAS
、IFUNC
和 COMDAT
的含義發生變化,使得前兩個運算元指定字串表中字串的偏移量和大小(請參閱 STRTAB_BLOCK 內容),函數名稱從值符號表中的 FNENTRY
紀錄中移除,且頂層 VALUE_SYMTAB_BLOCK
可能僅包含 FNENTRY
紀錄。
MODULE_CODE_TRIPLE 紀錄¶
[TRIPLE, ...string...]
TRIPLE
紀錄(代碼 2)包含可變數量的數值,表示 target triple
規格字串的位元組。
MODULE_CODE_DATALAYOUT 紀錄¶
[DATALAYOUT, ...string...]
DATALAYOUT
紀錄(代碼 3)包含可變數量的數值,表示 target datalayout
規格字串的位元組。
MODULE_CODE_ASM 紀錄¶
[ASM, ...string...]
ASM
紀錄(代碼 4)包含可變數量的數值,表示 module asm
字串的位元組,各個組裝區塊以換行符號(ASCII 10)字元分隔。
MODULE_CODE_SECTIONNAME 紀錄¶
[SECTIONNAME, ...string...]
SECTIONNAME
紀錄(代碼 5)包含可變數量的數值,表示單個區段名稱字串的位元組。對於模組內引用(例如,在全域變數或函數 section
屬性中)的每個區段名稱,都應該有一個 SECTIONNAME
紀錄。這些紀錄可以通過 GLOBALVAR
或 FUNCTION
紀錄的 *section* 欄位中的從 1 開始的索引來引用。
MODULE_CODE_DEPLIB 紀錄¶
[DEPLIB, ...string...]
DEPLIB
紀錄(代碼 6)包含可變數量的數值,表示單個相依程式庫名稱字串的位元組,即 deplibs
宣告中提及的程式庫之一。對於引用的每個程式庫名稱,都應該有一個 DEPLIB
紀錄。
MODULE_CODE_GLOBALVAR 紀錄¶
[GLOBALVAR, strtab offset, strtab size, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, unnamed_addr, externally_initialized, dllstorageclass, comdat, attributes, preemptionspecifier]
GLOBALVAR
紀錄(代碼 7)標記全域變數的宣告或定義。運算元欄位為
strtab offset, strtab size:指定全域變數的名稱。請參閱 STRTAB_BLOCK 內容。
pointer type:用於指向此全域變數的指標類型的類型索引
isconst:如果變數在模組中被視為常數,則為非零值;否則為零值
initid:如果為非零值,則為此變數的初始設定式的數值索引,加 1。
linkage:此變數的連結類型的編碼
external
:代碼 0weak
:代碼 1appending
:代碼 2internal
:代碼 3linkonce
:代碼 4dllimport
:代碼 5dllexport
:代碼 6extern_weak
:代碼 7common
:代碼 8private
:代碼 9weak_odr
:代碼 10linkonce_odr
:代碼 11available_externally
:代碼 12deprecated:代碼 13
deprecated:代碼 14
alignment*:變數請求對齊方式的以 2 為底的對數,加 1
section:如果為非零值,則為 MODULE_CODE_SECTIONNAME 條目表中從 1 開始的區段索引。
visibility:如果存在,則為此變數的可見性編碼
default
:代碼 0hidden
:代碼 1protected
:代碼 2
threadlocal:如果存在,則為變數的執行緒本機儲存模式的編碼
not thread local
:代碼 0thread local; default TLS model
:代碼 1localdynamic
:代碼 2initialexec
:代碼 3localexec
:代碼 4
unnamed_addr:如果存在,則為此變數的
unnamed_addr
屬性的編碼not
unnamed_addr
:代碼 0unnamed_addr
:代碼 1local_unnamed_addr
:代碼 2
dllstorageclass:如果存在,則為此變數的 DLL 儲存類別的編碼
default
:代碼 0dllimport
:代碼 1dllexport
:代碼 2
comdat:此函數的 COMDAT 的編碼
attributes:如果為非零值,則為 AttributeLists 表中從 1 開始的索引。
preemptionspecifier:如果存在,則為此變數的執行階段搶佔規範的編碼
dso_preemptable
:代碼 0dso_local
:代碼 1
MODULE_CODE_FUNCTION 紀錄¶
[FUNCTION, strtab offset, strtab size, type, callingconv, isproto, linkage, paramattr, alignment, section, visibility, gc, prologuedata, dllstorageclass, comdat, prefixdata, personalityfn, preemptionspecifier]
FUNCTION
紀錄(代碼 8)標記函數的宣告或定義。運算元欄位為
strtab offset, strtab size:指定函數的名稱。請參閱 STRTAB_BLOCK 內容。
type:描述此函數的函數類型的類型索引
callingconv:呼叫慣例編號:*
ccc
:代碼 0 *fastcc
:代碼 8 *coldcc
:代碼 9 *anyregcc
:代碼 13 *preserve_mostcc
:代碼 14 *preserve_allcc
:代碼 15 *swiftcc
:代碼 16 *cxx_fast_tlscc
:代碼 17 *tailcc
:代碼 18 *cfguard_checkcc
:代碼 19 *swifttailcc
:代碼 20 *x86_stdcallcc
:代碼 64 *x86_fastcallcc
:代碼 65 *arm_apcscc
:代碼 66 *arm_aapcscc
:代碼 67 *arm_aapcs_vfpcc
:代碼 68isproto*:如果此條目表示宣告而不是定義,則為非零值
linkage:此函數的 連結類型 的編碼
paramattr:如果為非零值,則為 PARAMATTR_CODE_ENTRY 條目表中從 1 開始的參數屬性索引。
alignment:函數請求對齊方式的以 2 為底的對數,加 1
section:如果為非零值,則為 MODULE_CODE_SECTIONNAME 條目表中從 1 開始的區段索引。
visibility:此函數的 可見性 的編碼
gc:如果存在且為非零值,則為 MODULE_CODE_GCNAME 條目表中從 1 開始的垃圾收集器索引。
unnamed_addr:如果存在,則為此函數的 unnamed_addr 屬性的編碼
prologuedata:如果為非零值,則為此函數的前言資料的數值索引,加 1。
dllstorageclass:此函數的 dllstorageclass 的編碼
comdat:此函數的 COMDAT 的編碼
prefixdata:如果為非零值,則為此函數的前綴資料的數值索引,加 1。
personalityfn:如果為非零值,則為此函數的 personality 函數的數值索引,加 1。
preemptionspecifier:如果存在,則為此函數的 執行階段搶佔規範 的編碼。
MODULE_CODE_ALIAS 紀錄¶
[ALIAS, strtab offset, strtab size, alias type, aliasee val#, linkage, visibility, dllstorageclass, threadlocal, unnamed_addr, preemptionspecifier]
ALIAS
紀錄(代碼 9)標記別名的定義。運算元欄位為
strtab offset, strtab size:指定別名的名稱。請參閱 STRTAB_BLOCK 內容。
alias type:別名的類型索引
aliasee val#:被別名的值的數值索引
linkage:此別名的 連結類型 的編碼
visibility:如果存在,則為別名的 可見性 的編碼
dllstorageclass:如果存在,則為別名的 dllstorageclass 的編碼
threadlocal:如果存在,則為別名的 執行緒本機屬性 的編碼
unnamed_addr:如果存在,則為此別名的 unnamed_addr 屬性的編碼
preemptionspecifier:如果存在,則為此別名的 執行階段搶佔規範 的編碼。
MODULE_CODE_GCNAME 紀錄¶
[GCNAME, ...string...]
GCNAME
紀錄(代碼 11)包含可變數量的數值,表示單個垃圾收集器名稱字串的位元組。對於函數 gc
屬性中引用的每個垃圾收集器名稱,都應該有一個 GCNAME
紀錄。這些紀錄可以通過 FUNCTION
紀錄的 *gc* 欄位中的從 1 開始的索引來引用。
PARAMATTR_BLOCK 內容¶
PARAMATTR_BLOCK
區塊(id 9)包含一個條目表,描述函數參數的屬性。這些條目通過從 1 開始的索引在模組區塊 FUNCTION 紀錄的 *paramattr* 欄位中,或在函數區塊 INST_INVOKE
和 INST_CALL
紀錄的 *attr* 欄位中引用。
PARAMATTR_BLOCK
內的條目被構造為確保每個條目都是唯一的(即,沒有兩個索引表示等效的屬性列表)。
PARAMATTR_CODE_ENTRY 紀錄¶
[ENTRY, attrgrp0, attrgrp1, ...]
ENTRY
紀錄(代碼 2)包含可變數量的數值,描述一組唯一的函數參數屬性。每個 attrgrp 值都用作鍵,以在 PARAMATTR_GROUP_BLOCK
區塊中描述的屬性群組表中查找條目。
PARAMATTR_CODE_ENTRY_OLD 紀錄¶
注意
這是 LLVM 3.2 及更早版本產生的屬性的舊版編碼。根據 IR 向後相容性 政策中的規定,保證當前 LLVM 版本可以理解它。
[ENTRY, paramidx0, attr0, paramidx1, attr1...]
ENTRY
紀錄(代碼 1)包含偶數個數值,描述一組唯一的函數參數屬性。每個 paramidx 值指示表示哪個屬性集,其中 0 表示傳回值屬性,0xFFFFFFFF 表示函數屬性,其他值表示從 1 開始的函數參數。每個 attr 值都是一個位元遮罩,具有以下解釋
位元 0:
zeroext
位元 1:
signext
位元 2:
noreturn
位元 3:
inreg
位元 4:
sret
位元 5:
nounwind
位元 6:
noalias
位元 7:
byval
位元 8:
nest
位元 9:
readnone
位元 10:
readonly
位元 11:
noinline
位元 12:
alwaysinline
位元 13:
optsize
位元 14:
ssp
位元 15:
sspreq
位元 16-31:
align n
位元 32:
nocapture
位元 33:
noredzone
位元 34:
noimplicitfloat
位元 35:
naked
位元 36:
inlinehint
位元 37-39:
alignstack n
,表示為請求對齊方式的以 2 為底的對數,加 1
PARAMATTR_GROUP_BLOCK 內容¶
PARAMATTR_GROUP_BLOCK
區塊(id 10)包含一個條目表,描述模組中存在的屬性群組。這些條目可以在 PARAMATTR_CODE_ENTRY
條目中引用。
PARAMATTR_GRP_CODE_ENTRY 紀錄¶
[ENTRY, grpid, paramidx, attr0, attr1, ...]
ENTRY
紀錄(代碼 3)包含 grpid 和 paramidx 值,後跟可變數量的數值,描述一組唯一的屬性群組。grpid 值是屬性群組的唯一鍵,可以在 PARAMATTR_CODE_ENTRY
條目中引用。paramidx 值指示表示哪個屬性集,其中 0 表示傳回值屬性,0xFFFFFFFF 表示函數屬性,其他值表示從 1 開始的函數參數。
每個 attr 本身都表示為可變數量的數值
kind, key [, ...], [value [, ...]]
每個屬性可以是眾所周知的 LLVM 屬性(可能帶有關聯的整數值),也可以是任意字串(可能帶有關聯的任意字串值)。kind 值是一個整數代碼,用於區分這些可能性
代碼 0:眾所周知的屬性
代碼 1:帶有整數值的眾所周知的屬性
代碼 3:字串屬性
代碼 4:帶有字串值的字串屬性
對於眾所周知的屬性(代碼 0 或 1),key 值是一個整數代碼,用於識別屬性。對於帶有整數參數的屬性(代碼 1),value 值指示參數。
對於字串屬性(代碼 3 或 4),key 值實際上是可變數量的數值,表示以空字元結尾的字串的位元組。對於帶有字串參數的屬性(代碼 4),value 值類似地是可變數量的數值,表示以空字元結尾的字串的位元組。
整數代碼映射到屬性,如 AttributeKindCodes
列舉中所述,該列舉位於檔案 LLVMBitCodes.h 中。
例如
enum AttributeKindCodes {
// = 0 is unused
ATTR_KIND_ALIGNMENT = 1,
ATTR_KIND_ALWAYS_INLINE = 2,
...
}
對應於
代碼 1:
align(<n>)
代碼 2:
alwaysinline
列舉和屬性名稱字串之間的映射可以在檔案 Attributes.td 中找到。
注意
allocsize
屬性對其參數具有特殊的編碼。它的兩個參數(都是 32 位元整數)被打包成一個 64 位元整數值(即 (EltSizeParam << 32) | NumEltsParam
),其中 NumEltsParam
在未指定時採用 sentinel 值 -1。
注意
vscale_range
屬性對其參數具有特殊的編碼。它的兩個參數(都是 32 位元整數)被打包成一個 64 位元整數值(即 (Min << 32) | Max
),其中 Max
在未指定時採用 Min
的值。
TYPE_BLOCK 內容¶
TYPE_BLOCK
區塊(id 17)包含構成類型運算元條目表的紀錄,該表用於表示 LLVM 模組中引用的類型。每個紀錄(除了 NUMENTRY 之外)生成一個單一的類型表條目,可以從指令、常數、元數據、類型符號表條目或其他類型運算元紀錄中通過從 0 開始的索引來引用。
TYPE_BLOCK
內的條目被構造為確保每個條目都是唯一的(即,沒有兩個索引表示結構等效的類型)。
TYPE_CODE_NUMENTRY 紀錄¶
[NUMENTRY, numentries]
NUMENTRY
紀錄(代碼 1)包含一個單一值,指示模組類型表中的類型代碼條目總數。如果存在,NUMENTRY
應該是區塊中的第一個紀錄。
TYPE_CODE_VOID 紀錄¶
[VOID]
VOID
紀錄(代碼 2)將 void
類型添加到類型表。
TYPE_CODE_HALF 紀錄¶
[HALF]
HALF
紀錄(代碼 10)將 half
(16 位元浮點數) 類型添加到類型表。
TYPE_CODE_BFLOAT 紀錄¶
[BFLOAT]
「BFLOAT
」記錄 (代碼 23) 將 bfloat
(16 位元 brain floating point) 類型新增至類型表。
TYPE_CODE_FLOAT 記錄¶
[FLOAT]
「FLOAT
」記錄 (代碼 3) 將 float
(32 位元浮點數) 類型新增至類型表。
TYPE_CODE_DOUBLE 記錄¶
[DOUBLE]
「DOUBLE
」記錄 (代碼 4) 將 double
(64 位元浮點數) 類型新增至類型表。
TYPE_CODE_LABEL 記錄¶
[LABEL]
「LABEL
」記錄 (代碼 5) 將 label
類型新增至類型表。
TYPE_CODE_OPAQUE 記錄¶
[OPAQUE]
「OPAQUE
」記錄 (代碼 6) 將 opaque
類型新增至類型表,其名稱由先前遇到的「STRUCT_NAME
」記錄所定義。請注意,不同的「opaque
」類型不會統一。
TYPE_CODE_INTEGER 記錄¶
[INTEGER, width]
「INTEGER
」記錄 (代碼 7) 將整數類型新增至類型表。單一 width 欄位表示整數類型的寬度。
TYPE_CODE_POINTER 記錄¶
[POINTER, pointee type, address space]
「POINTER
」記錄 (代碼 8) 將指標類型新增至類型表。運算元欄位為
pointee type:指向類型的類型索引
address space:如果提供,則為目標特定的編號位址空間,指向的物件駐留在該空間中。否則,預設位址空間為零。
TYPE_CODE_FUNCTION_OLD 記錄¶
注意
這是 LLVM 3.0 及更早版本產生的函式之舊版編碼。目前 LLVM 版本保證可以理解此編碼,如 IR 向後相容性 政策中所述。
[FUNCTION_OLD, vararg, ignored, retty, ...paramty... ]
「FUNCTION_OLD
」記錄 (代碼 9) 將函式類型新增至類型表。運算元欄位為
vararg:如果類型表示 varargs 函式,則為非零值
ignored:此值欄位僅為了向後相容性而存在,並且會被忽略
retty:函式傳回類型的類型索引
paramty:零或多個類型索引,表示函式的參數類型
TYPE_CODE_ARRAY 記錄¶
[ARRAY, numelts, eltty]
「ARRAY
」記錄 (代碼 11) 將陣列類型新增至類型表。運算元欄位為
numelts:此類型陣列中的元素數量
eltty:陣列元素類型的類型索引
TYPE_CODE_VECTOR 記錄¶
[VECTOR, numelts, eltty]
「VECTOR
」記錄 (代碼 12) 將向量類型新增至類型表。運算元欄位為
numelts:此類型向量中的元素數量
eltty:向量元素類型的類型索引
TYPE_CODE_X86_FP80 記錄¶
[X86_FP80]
「X86_FP80
」記錄 (代碼 13) 將 x86_fp80
(80 位元浮點數) 類型新增至類型表。
TYPE_CODE_FP128 記錄¶
[FP128]
「FP128
」記錄 (代碼 14) 將 fp128
(128 位元浮點數) 類型新增至類型表。
TYPE_CODE_PPC_FP128 記錄¶
[PPC_FP128]
「PPC_FP128
」記錄 (代碼 15) 將 ppc_fp128
(128 位元浮點數) 類型新增至類型表。
TYPE_CODE_METADATA 記錄¶
[METADATA]
「METADATA
」記錄 (代碼 16) 將 metadata
類型新增至類型表。
TYPE_CODE_X86_MMX 記錄¶
[X86_MMX]
「X86_MMX
」記錄 (代碼 17) 已棄用,並作為 <1 x i64> 向量匯入。
TYPE_CODE_STRUCT_ANON 記錄¶
[STRUCT_ANON, ispacked, ...eltty...]
「STRUCT_ANON
」記錄 (代碼 18) 將常值結構類型新增至類型表。運算元欄位為
ispacked:如果類型表示封裝結構,則為非零值
eltty:零或多個類型索引,表示結構的元素類型
TYPE_CODE_STRUCT_NAME 記錄¶
[STRUCT_NAME, ...string...]
「STRUCT_NAME
」記錄 (代碼 19) 包含可變數量的數值,代表結構名稱的位元組。下一個「OPAQUE
」或「STRUCT_NAMED
」記錄將使用此名稱。
TYPE_CODE_STRUCT_NAMED 記錄¶
[STRUCT_NAMED, ispacked, ...eltty...]
「STRUCT_NAMED
」記錄 (代碼 20) 將已識別的結構類型新增至類型表,其名稱由先前遇到的「STRUCT_NAME
」記錄所定義。運算元欄位為
ispacked:如果類型表示封裝結構,則為非零值
eltty:零或多個類型索引,表示結構的元素類型
TYPE_CODE_FUNCTION 記錄¶
[FUNCTION, vararg, retty, ...paramty... ]
「FUNCTION
」記錄 (代碼 21) 將函式類型新增至類型表。運算元欄位為
vararg:如果類型表示 varargs 函式,則為非零值
retty:函式傳回類型的類型索引
paramty:零或多個類型索引,表示函式的參數類型
TYPE_CODE_X86_AMX 記錄¶
[X86_AMX]
「X86_AMX
」記錄 (代碼 24) 將 x86_amx
類型新增至類型表。
TYPE_CODE_TARGET_TYPE 記錄¶
[TARGET_TYPE, num_tys, ...ty_params..., ...int_params... ]
「TARGET_TYPE
」記錄 (代碼 26) 將目標擴充類型新增至類型表,其名稱由先前遇到的「STRUCT_NAME
」記錄所定義。運算元欄位為
num_tys:作為類型的參數數量 (相對於整數)
ty_params:表示類型參數的類型索引
int_params:對應於整數參數的數字。
CONSTANTS_BLOCK 內容¶
「CONSTANTS_BLOCK
」區塊 (id 11) …
FUNCTION_BLOCK 內容¶
「FUNCTION_BLOCK
」區塊 (id 12) …
除了下面描述的記錄類型之外,「FUNCTION_BLOCK
」區塊可能包含以下子區塊
VALUE_SYMTAB_BLOCK 內容¶
「VALUE_SYMTAB_BLOCK
」區塊 (id 14) …
METADATA_BLOCK 內容¶
「METADATA_BLOCK
」區塊 (id 15) …
METADATA_ATTACHMENT 內容¶
「METADATA_ATTACHMENT
」區塊 (id 16) …
STRTAB_BLOCK 內容¶
「STRTAB
」區塊 (id 23) 包含單一記錄 (「STRTAB_BLOB
」,id 1),其中具有單一 blob 運算元,包含位元碼檔案的字串表。
字串表中的字串未以 null 終止。記錄的 strtab offset 和 strtab size 運算元指定字串在字串表中的位元組偏移量和大小。
字串表由位元碼檔案中所有先前的區塊使用,這些區塊之後沒有其他介入的 「STRTAB
」區塊。通常,一個位元碼檔案會有單一字串表,但如果它是透過二進制串連多個位元碼檔案而建立的,則可能有多個字串表。