LLVM MC层机器编码

发布时间 2023-05-29 05:02:27作者: 吴建明wujianming

LLVM MC层机器编码

1. MC 层概述

MC 层被用来表示机器编码级别的代码,相比于更高层的表示形式,它去除了像常量池、跳转表、全局变量等概念。在这一层上,LLVM 处理如 Label 名称、机器指令、目标文件段信息。这一层的代码在一些阶段非常重要,比如代码生成的最后阶段(也就是写入 .s 或 .o 文件时),或者被 llvm-mc 工具用来作为独立汇编器和反汇编器使用。

描述一些很重要的类,在之后的内容,还会描述和这一层交互的其他很重要的子系统。

2. MCStreamer API

MCStreamer API 被认为是汇编器中设计最好的 API。它作为一个抽象的接口,被实现为不同的功能(比如输出 .s 文件、输出 ELF 格式的 .o 文件等),但是它的 API 直接对应于在 .s 文件中对应的内容。MCStreamer 类中,对于每一类指示符(directive,和 instruction 有区分)都有一个对应的方法来处理,比如 EmitLabel,EmitSymbolAttribute,SwitchSection,EmitValue 等,这些都是汇编级别的指示符。它还提供一个 EmitInstruction 方法,用来将 MCInst 结构的代码输出到流。

对于 llvm-mc 独立汇编器和代码生成器来说,这个类都非常重要。llvm-mc 工具实际上就是一个汇编解析器加 MCStreamer 的输出;而在代码生成器中,Code Emission 阶段利用 MCStreamer 将高级别的 LLVM IR 和 Machine* 结构构造成低层次的 MC 层结构,同时也是通过 MCStreamer 类发出指令。

从 MCStreamer 的实现角度看,有两大块非常重要的部分:一个是写出 .s 文件(子类 MCAsmStreamer),另一个是写出 .o 文件(子类 MCObjectStreamer)。MCAsmStreamer 通过每一个 Emit* 方法直接输出指令,但对于 MCObjectStreamer,还需要实现一个完整的汇编器的功能。

对于目标相关的指令,MCStreamer 会依赖于一个 MCTargetStreamer 实例。每一个目标都会继承这个类来实现一个子类,每一个方法都会有两种多态的继承实现,也就是一个输出 object 的 streamer 和一个输出 asm 的 streamer。通过后者发射指示符(比如 EmitFnStart 发射 .fnstart),通过前者发射汇编的逻辑代码。

为了让 LLVM 顺利使用这个类,在目标初始化时,必须通过 TargetRegistry::RegisterAsmStreamer 和 TargetRegistry::RegisterMCObjectStreamer 两个回调函数来分配 MCStreamer 的 streamer 基类对象,并将其传递给 createAsmStreamer 等位置来构造目标 streamer 子对象。

3. MCContext 类

MCContext 类拥有 MC 层很多唯一性的数据结构,包括 symbols、sections 等。所以,可以与这个类来进行交互,实现创建 symbols 和 sections 的目的。这个类没有子类继承。

4. MCSymbol 类

MCSymbol 类用来表示一个汇编文件中 symbol 的结构(也就是 label)。有两种类型,一种是汇编器临时的符号,另一种是普通符号。汇编器临时符号被汇编器用来做一些处理,但会在输出目标文件时被删去。通常这种符号会在 label 开头名字加一个前缀符号,比如在 MachO 平台下,会有一个 L 前缀符号的 label 用来表示临时符号。

MCSymbols 只被 MCContext 使用来创建符号。这意味着可以通过指针运算来检查两个符号是否是同一个。但需要注意,两个指针不相等并不表示两个符号被放在不同的地址(两个符号可能在同一个目标文件地址),比如在汇编文件中有如下结构:

foo:
bar:
  .byte 4

foo 和 bar 这两个 label 是在同一个目标文件地址的(但他们是不同符号)。

5. MCSection 类

MCSection 类用来表示目标文件中的 section段,对于不同的目标文件,这个类会被继承为不同的子类(比如 MCSectionMachO,MCSectionCOFF,MCSectionELF)。同样的,它也只被 MCContext 使用来创建段。MCStreamer 有一个对于当前段的标记,可以通过 SwitchToSection 来改变段标记。

6. MCInst 类

MCInst 类是目标无关的指令表示,相比于 MachineInstr,它更为简单,它维护着每一条机器指令的指令码和指令操作数的 vector。MCOperand(指令操作数)有三种不同的类型:一个简单立即数、一个目标寄存器的 ID 或一个符号表示(MCExpr 类型,比如 "Lfoo-Lbar+42")。

MCInst 现在通常被用来表示 MC 层的机器指令。它被指令编码、指令输出、汇编 parser 的类型生成和反汇编器使用。