IRTranslator¶
此 Pass 将输入的 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 语言参考手册 部分足以满足许多编译需求,但并不完全。希望编译包含某些较少见功能的 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
使用多个 vreg。
TODO
:由于某些位是未定义的(填充),我们应该考虑使用其他元数据来增强表示(实际上,在 vreg 上缓存 computeKnownBits 信息)。请参阅 PR26161:[GlobalISel] 在 IR 到 MachineInstr 转换期间将聚合类型的值转换为 vreg
常量的翻译¶
常量操作数被翻译为对由 G_CONSTANT
或 G_FCONSTANT
指令定义的虚拟寄存器的使用。这些指令放置在入口块中,以使它们能够受到连续 CSE 实现(CSEMIRBuilder
)的影响。删除了它们的调试位置信息,以防止这导致调试器混淆。
这很有益,因为它允许我们在 InstructionSelect 期间将常量折叠到立即数操作数中,同时仍然避免对昂贵的不可折叠常量进行冗余物化。但是,这可能会导致 -O0 流水线中出现不必要的溢出和重新加载,因为这些虚拟寄存器可能具有较长的生命周期范围。可以通过在转换器之后运行 本地化器 来缓解这种情况。