IRTranslator¶
此階段將輸入的 LLVM-IR Function
轉譯為 通用機器碼 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 的使用者可能需要實作轉譯。
目標內建函式¶
關於是否要為轉譯目標內建函式添加目標鉤子,已經有一些(非公開)的爭論。在參與討論的人中,普遍同意 IRTranslator 應該能夠以可自訂的方式降低目標內建函式,但在撰寫本文時,尚未進行任何實作。
轉譯函式呼叫¶
IRTranslator
還通過將呼叫、返回和參數降低到適當的物理寄存器使用和指令序列來實現 ABI 的呼叫約定。這是通過使用 CallLowering
介面實現的,該介面提供了一些目標應該實作的鉤子:lowerFormalArguments
、lowerReturn
、lowerCall
等。
本質上,所有這些鉤子都需要找到一種方法,將參數/返回值在函式其餘部分使用的虛擬寄存器和 ABI 規定的物理寄存器或堆疊之間移動。這可能涉及將大型類型拆分為較小的類型、引入符號/零擴展等。為了在不同後端之間儘可能多地共用這些代碼,CallLowering
提供了一些輔助工具和介面
ArgInfo
- 用於形式參數,但也用於返回值、實際參數和呼叫結果;包含 IR 類型、虛擬寄存器等資訊;大型值可能必須拆分為多個ArgInfo
物件(CallLowering::splitToValueTypes
可以幫助完成此操作);「
ValueAssigner
」- 使用一個CCAssignFn
(通常由 TableGen 產生,請參閱呼叫慣例)來決定放置每個ArgInfo
(實體暫存器或堆疊)的位置;後端可以使用提供的IncomingValueAssigner
(用於形式參數和呼叫結果)和OutgoingValueAssigner
(用於實際參數和函式回傳值),但也可以將它們子類化;「
ValueHandler
」- 插入必要的指令,將每個值放到它應該在的位置;它具有用於將值賦值給暫存器或地址的純虛擬方法,以及許多其他輔助方法;「
determineAndHandleAssignments
」(或用於更精細的控制,determineAssignments
和handleAssignments
)- 包含一些樣板程式碼,用於在一系列ArgInfo
物件上叫用給定的ValueAssigner
和ValueHandler
。
聚合¶
注意
這一點自撰寫以來已有所變更,不再準確。在這次改進文件時,它沒有被更新,因為我沒有在程式碼庫的這個部分做太多工作,應該由更了解它的人來處理。
聚合被降低為多個虛擬暫存器,類似於 SelectionDAG 通過 GetValueVTs
的多個虛擬暫存器。
TODO
:由於某些位是未定義的(填補),我們應該考慮使用額外的中繼資料來擴充表示(實際上是在虛擬暫存器上快取 computeKnownBits 資訊)。請參閱PR26161:[GlobalISel] 在 IR 到 MachineInstr 轉換過程中,聚合類型的值到虛擬暫存器
常數轉換¶
常數運算元被轉換為使用由 G_CONSTANT
或 G_FCONSTANT
指令定義的虛擬暫存器。這些指令被放置在入口區塊中,以便它們可以受到連續 CSE 實作(CSEMIRBuilder
)的影響。它們的除錯位置資訊被移除,以防止這混淆除錯器。
這樣做的好處是,它允許我們在指令選擇期間將常數摺疊到立即運算元中,同時仍然避免為昂貴的不可摺疊常數產生冗餘的實體化。但是,這可能會導致 -O0 管線中出現不必要的溢出和重新載入,因為這些虛擬暫存器可能具有很長的存活期。這可以通過在轉換器之後執行本地化器來減輕。