IRTranslator¶
此 pass 將輸入的 LLVM-IR Function
翻譯成 Generic Machine IR MachineFunction
。這通常是直接翻譯,但偶爾會變得更複雜。例如
%2 = add i32 %0, %1
變成
%2:_(s32) = G_ADD %0:_(s32), %1:_(s32)
反之
call i32 @puts(i8* %cast210)
會根據目標的 ABI 規則進行翻譯。
注意
目前實作的 LLVM 語言參考手冊 部分內容已足以應付許多編譯,但並非 100% 完整。尋求編譯包含某些較罕見功能的 LLVM-IR 的使用者可能需要自行實作翻譯。
目標內建函式¶
關於是否為翻譯目標內建函式新增目標 hooks,曾有一些(非公開列表)的討論。在參與討論的人中,普遍認為 IRTranslator 應該能夠以可自訂的方式降低目標內建函式,但在撰寫本文時,尚未有任何實作工作。
翻譯函數呼叫¶
IRTranslator
也透過將呼叫、返回和引數降低到適當的實體暫存器使用和指令序列,來實作 ABI 的呼叫慣例。這是透過使用 CallLowering
介面實現的,該介面提供了一些目標應實作的 hooks:lowerFormalArguments
、lowerReturn
、lowerCall
等。
本質上,所有這些 hooks 都需要找到一種方法,根據 ABI 的規定,在函數其餘部分使用的虛擬暫存器與實體暫存器或堆疊之間移動引數/返回值。這可能涉及將大型型別分割成較小的型別、引入符號/零擴展等。為了在不同的後端之間盡可能多地共享此程式碼,CallLowering
提供了一些 helper 和介面
ArgInfo
- 用於形式引數,但也用於返回值、實際引數和呼叫結果;包含諸如 IR 型別、虛擬暫存器等資訊;大型值可能必須分割成多個ArgInfo
物件 (CallLowering::splitToValueTypes
可以協助處理此問題);ValueAssigner
- 使用CCAssignFn
,通常由 TableGen 產生 (請參閱呼叫慣例),以決定將每個ArgInfo
放置在哪裡 (實體暫存器或堆疊);後端可以使用提供的IncomingValueAssigner
(用於形式引數和呼叫結果) 和OutgoingValueAssigner
(用於實際引數和函數返回),但也可以將它們子類化;ValueHandler
- 插入必要的指令,將每個值放置在它所屬的位置;它具有用於將值指派給暫存器或位址的 pure virtual methods,以及許多其他 helpers;determineAndHandleAssignments
(或對於更精細的控制,determineAssignments
和handleAssignments
) - 包含一些用於在一系列ArgInfo
物件上調用給定的ValueAssigner
和ValueHandler
的樣板程式碼。
聚合¶
警告
自撰寫以來,這部分已變更,不再準確。在這次改進文件說明的過程中,尚未更新此部分,因為我沒有在此程式碼庫的這部分工作太多,應該由更了解它的人員注意。
聚合會被降低到多個虛擬暫存器中,類似於 SelectionDAG 透過 GetValueVTs
實現的多個 vregs。
TODO
:由於某些位元是 undef (填充),我們應該考慮使用額外的 metadata 擴充表示法 (實際上是在 vregs 上快取 computeKnownBits 資訊)。請參閱 PR26161:[GlobalISel] Value to vreg during IR to MachineInstr translation for aggregate type
常數轉譯¶
常數運算元會被翻譯為對虛擬暫存器的使用,該虛擬暫存器由 G_CONSTANT
或 G_FCONSTANT
指令定義。這些指令放置在 entry block 中,以便它們可以受到持續 CSE 實作 (CSEMIRBuilder
) 的影響。它們的 debug location 資訊會被移除,以防止這混淆偵錯器。
這很有益,因為它允許我們在 InstructionSelect 期間將常數摺疊到 immediate 運算元中,同時仍然避免為昂貴的不可摺疊常數進行多餘的具體化。然而,這可能會導致在 -O0 管線中產生不必要的 spills 和 reloads,因為這些虛擬暫存器可能具有較長的存活範圍。這可以透過在翻譯器之後執行 localizer 來緩解。