通用機器 IR

通用機器 IR (gMIR) 是一種與 機器 IR (MIR) 共用相同資料結構的中間表示法,但具有較寬鬆的限制。隨著編譯流程的進行,這些限制會逐漸收緊,直到 gMIR 變成 MIR。

本文檔的其餘部分將假設您熟悉 機器 IR (MIR) 中的概念,並將重點說明 MIR 和 gMIR 之間的差異。

通用機器指令

備註

本節擴充 MIR 語言參考中的 機器指令

MIR 主要處理目標指令,並且只有一組少量的目標獨立操作碼,例如 COPYPHIREG_SEQUENCE,而 gMIR 定義了一組豐富的 通用操作碼,它們是目標獨立的,並描述通常由目標支援的操作。一個例子是 G_ADD,它是整數加法的通用操作碼。您可以在 通用操作碼 中找到有關每個通用操作碼的更多資訊。

MachineIRBuilder 類別包裝了 MachineInstrBuilder,並提供了一種方便的方式來建立這些通用指令。

通用虛擬暫存器

備註

本節擴充 MIR 語言參考中的 暫存器

通用虛擬暫存器類似於虛擬暫存器,但未被分配暫存器類別限制。相反,通用虛擬暫存器具有較不嚴格的限制,從 低階型別 開始,然後進一步限制為 暫存器組。最終,它們將被限制為一個暫存器類別,此時它們將成為普通的虛擬暫存器。

通用虛擬暫存器可以使用 MachineRegisterInfo 提供的所有虛擬暫存器 API。特別是,定義-使用鏈 API 可以使用,而無需將它們與非通用虛擬暫存器區分開來。

為簡單起見,大多數通用指令僅接受虛擬暫存器(包括通用和非通用)。有一些例外,但一般來說

  • 它們不會使用立即數,而是使用由指令定義的泛型虛擬暫存器來實現立即數值(請參閱常數的轉換)。通常這是 G_CONSTANT 或 G_FCONSTANT。此規則的一個例外是 G_SEXT_INREG,它必須使用立即數。

  • 它們不會使用實體暫存器,而是使用泛型虛擬暫存器,該暫存器由從實體暫存器COPY定義,或由定義實體暫存器的COPY使用。

歷史註記

我們最初使用了一種替代表示法,其中 MRI 跟踪每個泛型虛擬暫存器的大小,並且指令具有類型列表。這有兩個缺陷:類型和大小是冗餘的,並且沒有通用的方法來獲取給定運算元的類型(因為指令類型和運算元之間沒有 1:1 的映射)。我們考慮將類型放在 MCInstrDesc 的某些變體中:請參閱PR26576:[GlobalISel] 泛型 MachineInstrs 需要一個類型,但這會增加相關物件的記憶體占用量

暫存器 Bank

暫存器 Bank 是由目標定義的一組暫存器類別。這個定義相當寬鬆,讓我們來談談它們可以實現什麼。

假設我們有一個處理器,它有兩個暫存器檔案,A 和 B。它們在各方面都是相同的,並以相同的成本支持相同的指令。它們只是物理上分開存放,每條指令只能訪問來自 A 或 B 的暫存器,而不能訪問兩者的混合。如果我們想對分散在兩個暫存器檔案中的數據執行操作,我們必須首先將所有數據複製到一個暫存器檔案中。

給定這樣一個處理器,我們將受益於將相關數據聚類到一個暫存器檔案中,以便我們最大限度地減少來回複製數據以滿足所有指令(可能相互衝突)的要求的成本。暫存器 Bank 是一種限制暫存器分配器為虛擬暫存器使用特定暫存器檔案的方法。

在實踐中,暫存器檔案 A 和 B 很少相同。它們通常可以存儲相同的數據,但通常對您可以在每個暫存器檔案上執行的操作有一些限制。一種相當常見的模式是其中一個可以訪問整數運算,而另一個可以訪問浮點運算。為了適應這種情況,讓我們將 A 和 B 重命名為 GPR(通用暫存器)和 FPR(浮點暫存器)。

我們現在有一些限制我們的額外約束。像 G_FMUL 這樣的操作必須在 FPR 中進行,而 G_ADD 必須在 GPR 中進行。然而,即使這規定了許多分配,我們仍然有一些自由。G_LOAD 可以在 GPR 和 FPR 中進行,我們想要哪一個取決於誰將使用加載的數據。類似地,G_FNEG 可以在 GPR 和 FPR 中進行。如果我們將其分配給 FPR,那麼我們將使用浮點求反。但是,如果我們將其分配給 GPR,那麼我們可以等效地將符號位與 1 進行 G_XOR 以反轉它。

總之,暫存器 Bank 是一種基於對在給定上下文中應用每個選擇時的差異進行一些分析來消除看似等效的選擇之間的歧義的方法。

舉一些具體的例子

AArch64

AArch64 有三個主要的 Bank。GPR 用於整數運算,FPR 用於浮點運算以及 NEON 向量指令集。第三個是 CCR,它描述了用於預測的條件碼暫存器。

MIPS

MIPS 有五個主要的暫存器庫,許多程式實際上只會用到其中一兩個。GPR 是一般用途的整數運算暫存器庫。FGR 或 CP1 用於浮點數運算,以及 MSA 向量指令和一些其他應用程式特定的擴充功能。CP0 用於系統暫存器,很少有程式會用到它。CP2 和 CP3 則是用於晶片中可能存在的任何應用程式專用協同處理器。可以說,LO 和 HI 暫存器也構成第六個暫存器庫,但它們只用於少數運算的結果,而且將其與 GPR 區分開來建模的價值值得商榷。

X86

X86 可以看作具有 3 個主要的暫存器庫:通用暫存器、x87 和向量暫存器(可以根據單精度指令與雙精度指令的域進一步劃分為每個域一個暫存器庫)。看起來似乎還有一些其他的潛在暫存器庫,例如 AVX512 遮罩暫存器專用的暫存器庫。

暫存器庫由目標提供的 API RegisterBankInfo 描述。

低階類型

此外,每個通用的虛擬暫存器都有一個類型,由 LLT 類別的實例表示。

EVT/MVT/Type 類似,它不區分無符號和有符號整數類型。此外,它也不區分整數和浮點數類型:它主要傳達絕對必要的資訊,例如大小和向量通道數

  • sN 代表純量

  • pN 代表指標

  • <N x sM> 代表向量

LLT 旨在取代 SelectionDAG 中 EVT 的使用。

以下是一些 LLT 範例及其 EVTType 等效項

LLT

EVT

IR 類型

s1

i1

i1

s8

i8

i8

s32

i32

i32

s32

f32

float

s17

i17

i17

s16

N/A

{i8, i8} [1]

s32

N/A

[4 x i8] [1]

p0

iPTR

i8*, i32*, %opaque*

p2

iPTR

i8 addrspace(2)*

<4 x s32>

v4f32

<4 x float>

s64

v1f64

<1 x double>

<3 x s32>

v3i32

<3 x i32>

原理:指令已經編碼了對類型的特定解釋(例如,addfadd,或 sdivudiv)。在類型系統中也編碼該信息需要引入位元轉換,而對選擇器沒有任何實際優勢。

指針類型由地址空間區分。這與 IR 相匹配,而不是 SelectionDAG,其中地址空間是操作上的屬性。這種表示更好地支持指針根據其地址空間具有不同的大小。

備註

注意事項

這仍然正確嗎?我以為我們已經刪除了 1 元素向量的概念。假設,它可能與標量不同,但我認為我們未能找到一個真實的例子。

目前,LLT 要求向量中至少有 2 個元素,但某些目標具有「1 元素向量」的概念。將它們表示為其底層標量類型是一個很好的簡化。

註腳

通用操作碼參考

可用的通用操作碼在 通用操作碼 中描述。