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 语言参考手册 当前已实现的部分足以满足许多编译需求,但并非 100% 完整。 寻求编译包含某些较少见功能的 LLVM-IR 的用户可能需要实现转换。

目标内建函数

关于是否为转换目标内建函数添加目标钩子,已经进行了一些(非公开)讨论。在那些讨论过这个问题的人中,普遍认为 IRTranslator 应该能够以可自定义的方式降低目标内建函数,但在撰写本文时,尚未开展任何工作来实现这一点。

转换函数调用

IRTranslator 还通过降低调用、返回和参数到适当的物理寄存器使用和指令序列来实现 ABI 的调用约定。 这是通过使用 CallLowering 接口实现的,该接口提供了目标应实现的几个钩子:lowerFormalArgumentslowerReturnlowerCall 等。

本质上,所有这些钩子都需要找到一种方法来在函数其余部分中使用的虚拟寄存器与物理寄存器或堆栈之间移动参数/返回值,这由 ABI 决定。 这可能涉及将大型类型拆分为较小的类型,引入符号/零扩展等。为了在不同的后端之间尽可能多地共享此代码,CallLowering 提供了一些助手和接口

  • ArgInfo - 用于形式参数,但也用于返回值、实际参数和调用结果;包含诸如 IR 类型、虚拟寄存器等信息;大型值可能必须拆分为多个 ArgInfo 对象(CallLowering::splitToValueTypes 可以对此有所帮助);

  • ValueAssigner - 使用 CCAssignFn,通常由 TableGen 生成(请参阅 调用约定),以决定将每个 ArgInfo 放在哪里(物理寄存器或堆栈);后端可以使用提供的 IncomingValueAssigner(用于形式参数和调用结果)和 OutgoingValueAssigner(用于实际参数和函数返回),但也可以对其进行子类化;

  • ValueHandler - 插入必要的指令,用于将每个值放在其所属的位置;它具有用于将值分配给寄存器或地址的纯虚方法,以及许多其他助手;

  • determineAndHandleAssignments (或为了更精细的控制,determineAssignmentshandleAssignments)- 包含一些样板代码,用于在一系列 ArgInfo 对象上调用给定的 ValueAssignerValueHandler

聚合类型

注意

自从编写以来,这已发生更改,并且不再准确。 在改进文档的此过程中,它尚未刷新,因为我在此代码库的这一部分没有做太多工作,并且应该引起对此更了解的人的注意。

聚合类型被降低为多个虚拟寄存器,类似于 SelectionDAG 的通过 GetValueVTs 的多个 vreg。

TODO:由于某些位是 undef(填充),我们应该考虑使用额外的元数据来增强表示(实际上,在 vreg 上缓存 computeKnownBits 信息)。 请参阅 PR26161:[GlobalISel] 用于聚合类型的 IR 到 MachineInstr 转换期间的值到 vreg

常量转换

常量操作数被转换为虚拟寄存器的使用,该虚拟寄存器由 G_CONSTANTG_FCONSTANT 指令定义。 这些指令放置在入口块中,以使其可以受到连续 CSE 实现 (CSEMIRBuilder) 的影响。 它们的调试位置信息被删除,以防止这使调试器感到困惑。

这很有益,因为它允许我们在 指令选择 期间将常量折叠到立即操作数中,同时仍然避免为昂贵的不可折叠常量进行冗余物化。 但是,这可能会导致 -O0 流水线中不必要的溢出和重新加载,因为这些虚拟寄存器可能具有较长的生命周期。 可以通过在转换器之后运行 localizer 来缓解这种情况。