借助LLVM的MCCodeEmitter完成指令编码 - sbw Blog

借助LLVM的MCCodeEmitter完成指令编码

来源: sbw Blog | 浏览: 91 | 评论: 0 发表时间: 2023-02-12

编译器后端中指令的编码是一项非常繁杂、易错的工作。需要查找大量的手册资料才能拼接出二进制序列,并且测试成本也比较高。于是研究了下,使用LLVM的模块完成指令的编码,自己的编译器这端只需要输出到符号化的汇编指令即可,大大减少了查文档拼二进制这项令人头秃的工作。



MCCodeEmitter

通过学习LLVM中X86等后端的代码,可以大概看出MCCodeEmitter的用法:


1. 先选定目标平台的Triple,创建出SubTargetInfo

2. 由此Triple创建出MCInstrInfoMCRegInfo,分别是目标平台的指令与寄存器的信息,因为即使同为x86,也存在很多种不同的CPU特性集,这些信息的目的是保证生成指令限制在目标平台所支持的范围。

3. 创建MCContext上下文对象

4. 创建MCCodeEmitterMCInst指令进行编码,得到二进制序列


示例代码:


InstrInfo & RegInfo

InstrInfoRegInfo都是由tablegen工具根据对应的*.td文件生成的,如根据X86.td生成的X86Registers.inc提供了X86的寄存器信息,如X86::RAX。而X86InstrInfo则提供了X86的指令操作符的枚举列表,如X86::PUSH64r,代表PUSH一个64位寄存器的操作指令。


默认情况下tablegen生成的这些枚举都是不对外的,需要在包含这些文件前手动Define一个GET_REGINFO_ENUMGET_INSTRINFO_ENUM来获取对应的枚举信息。


示例代码:


MCInst & MCInstBuilder

MCInstBuilder是一个Builder模式的类,用于创建一个对象化的机器指令MCInstMCInst中包含了操作符、每个操作数等信息。将MCInst传给MCCodeEmitter进行编码,即可得到这条指令的二进制编码信息。


需要注意的是,这里的MCInst是和对应*.td文件中定义的指令的操作数对应的,而不是和目标平台的操作码、操作数对应的,因为不同的指令集可能有各种的编码方案,LLVM统一使用*.td文件对不同平台的指令重新进行了建模分类,这里的操作符枚举也是由LLVM定义的,而不是由对应平台定义的。


示例:将push rax这条指令编码为二进制


指令的tablegen描述

以上面的push rax为例,它在X86InstrInfo.td的定义如下:


这是一条非常简单的指令,它只接受一个GR64类型的$reg参数,即64位寄存器号。


但通常,使用MCInstrBuilder创建一个指令需要的参数并不和实际指令对应,这也和LLVM自己对指令的封装方式有关,例如add rbx, 1这条指令:


就需要3个操作数,其中RBX重复了2遍。同时,在X86*.td文件中,是无法直接找到ADD64ri8这条指令的定义的,因为它是从ArithBinOp_RF这个类似于模板的类上生成出来的,而ArithBinOp_RF中又引用了BinOpRI8_RF


到这里,就可以看到由于LLVM抽象了所有2元操作的定义,所以导致即使是单寄存器与i8的加法,也被生成为一个具有$src1$src2$dst这3个操作数的描述形式了。为了避免歧义,这里针对这条指令添加了一个Constraints约束,即$src1 = $dst,所以在构造时,2个寄存器必须传成相同的寄存器。


通过借助LLVM完成指令编码,虽然减少了直接查各个CPU文档带来的麻烦,但是需要非常了解LLVM对每个平台的指令建模分类形式,同时需要引用LLVMtablegen生成的各个枚举值。由于这些*.td文件的结构不是固定的,导致LLVMMCInst指令格式以及生成的RegInfoInstInfo的枚举值也是高度依赖固定结构的,在编译时最好直接依赖LLVM一起编译,与最新生成的tablegen信息共同构建,从而保证这些生成的信息能够与LLVM相对应上。


后面,可能会尝试更具有通用和稳定性的方法,如自己生成成汇编描述,由LLVM完成标准的汇编描述到指令编码的转换,这样标准的汇编描述就可以作为一个稳定的结构,解偶LLVMtablegen数据。


本文的示例代码:https://github.com/sbwtw/LLVMTest




没有人评论过此文,还不快抢个沙发
  • 昵称: *
  • 邮箱:
  • 网址:
  • 记住我的信息
  • Color
  • Red
  • Blue
  • Code
  • bash
  • cpp
  • css
  • java
  • js
  • perl
  • php
  • python
  • ruby
  • sql
  • xml