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 的集合為:

縮寫 ID 4 及以上由串流本身定義,並指定 縮寫記錄編碼

區塊

位元流中的區塊表示串流的巢狀區域,並由內容特定的 ID 號碼識別(例如,LLVM IR 使用 ID 12 來表示函數主體)。區塊 ID 0-7 保留給 標準區塊,其含義由 Bitcode 定義;區塊 ID 8 及以上是應用程式特定的。巢狀區塊捕獲編碼在其中的資料的階層結構,並且在解析檔案時,各種屬性與區塊相關聯。區塊定義允許讀取器在恆定時間內有效率地跳過區塊(如果讀取器想要區塊的摘要,或者它想要有效率地跳過它不理解的資料)。LLVM IR 讀取器使用此機制來跳過函數主體,並根據需要延遲讀取它們。

在讀取和編碼串流時,會為區塊維護多個屬性。特別是,每個區塊都維護:

  1. 目前的縮寫 ID 寬度。此值在串流開始時為 2,並且每次輸入區塊記錄時都會設定。區塊條目指定區塊主體的縮寫 ID 寬度。

  2. 一組縮寫。縮寫可以在區塊內定義,在這種情況下,它們僅在該區塊中定義(子區塊和封閉區塊都看不到縮寫)。縮寫也可以在 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 時)。

  1. 文字運算元 — [11, litvaluevbr8] — 文字運算元指定結果中的值始終是單個特定值。此特定值在指示它是文字運算元的位元之後作為 vbr8 發出。

  2. 沒有資料的編碼資訊 — [01, encoding3] — 沒有額外資料的運算元編碼僅作為其代碼發出。

  3. 具有資料的編碼資訊 — [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]

這些值是:

  1. 第一個值 4 是此縮寫的縮寫 ID。

  2. 第二個值 2 是 LLVM IR 檔案 MODULE_BLOCK 區塊中 TRIPLE 記錄的記錄代碼。

  3. 第三個值 4 是陣列的長度。

  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_INTEGERCST_CODE_WIDE_INTEGER 記錄。它也用於 MODULE_CODE_VERSION 1 中的 phi 指令運算元。

LLVM IR 區塊

LLVM IR 定義了以下區塊

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 中,模組紀錄 FUNCTIONGLOBALVARALIASIFUNCCOMDAT 的含義發生變化,使得前兩個運算元指定字串表中字串的偏移量和大小(請參閱 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 紀錄。這些紀錄可以通過 GLOBALVARFUNCTION 紀錄的 *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:代碼 0

    • weak:代碼 1

    • appending:代碼 2

    • internal:代碼 3

    • linkonce:代碼 4

    • dllimport:代碼 5

    • dllexport:代碼 6

    • extern_weak:代碼 7

    • common:代碼 8

    • private:代碼 9

    • weak_odr:代碼 10

    • linkonce_odr:代碼 11

    • available_externally:代碼 12

    • deprecated:代碼 13

    • deprecated:代碼 14

  • alignment*:變數請求對齊方式的以 2 為底的對數,加 1

  • section:如果為非零值,則為 MODULE_CODE_SECTIONNAME 條目表中從 1 開始的區段索引。

  • visibility:如果存在,則為此變數的可見性編碼

    • default:代碼 0

    • hidden:代碼 1

    • protected:代碼 2

  • threadlocal:如果存在,則為變數的執行緒本機儲存模式的編碼

    • not thread local:代碼 0

    • thread local; default TLS model:代碼 1

    • localdynamic:代碼 2

    • initialexec:代碼 3

    • localexec:代碼 4

  • unnamed_addr:如果存在,則為此變數的 unnamed_addr 屬性的編碼

    • not unnamed_addr:代碼 0

    • unnamed_addr:代碼 1

    • local_unnamed_addr:代碼 2

  • dllstorageclass:如果存在,則為此變數的 DLL 儲存類別的編碼

    • default:代碼 0

    • dllimport:代碼 1

    • dllexport:代碼 2

  • comdat:此函數的 COMDAT 的編碼

  • attributes:如果為非零值,則為 AttributeLists 表中從 1 開始的索引。

  • preemptionspecifier:如果存在,則為此變數的執行階段搶佔規範的編碼

    • dso_preemptable:代碼 0

    • dso_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:代碼 68

  • isproto*:如果此條目表示宣告而不是定義,則為非零值

  • 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_INVOKEINST_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)包含 grpidparamidx 值,後跟可變數量的數值,描述一組唯一的屬性群組。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 offsetstrtab size 運算元指定字串在字串表中的位元組偏移量和大小。

字串表由位元碼檔案中所有先前的區塊使用,這些區塊之後沒有其他介入的 「STRTAB」區塊。通常,一個位元碼檔案會有單一字串表,但如果它是透過二進制串連多個位元碼檔案而建立的,則可能有多個字串表。