
LLVM MC 简介
本文针对 LLVM MC做了简单的介绍。受限于笔者知识水平,文中可能会存在某些理解身上的偏差,欢迎批评指正。
1. 整体介绍
LLVM MC (machine code) 层位于LLVM的底层,主要功能是负责汇编 (assembly)、反汇编 (disassembly)、以及生成二进制文件。作为LLVM的子项目,可以通过llvm-mc、llvm-objudump等工具直接操纵MC层。LLVM MC 的核心是引入了新的'MCInst'类来表示一个带有操作数的指令,这与代码生成器现有的指令概念'MachineInstr'不同。LLVM MC 整体框架如下图所示:
按照输入不同主要分为两条路径:
-
输入为汇编文件,经过Assembly Parser将汇编文件中的指令解析成Operand,然后通过MCTargetAsmParser将Operand转换成对应的MCInst,最终进入到MCStreamer,通过Instruction Encoder生成二进制文件。
-
输入为二进制文件,经过Instruction Decoder,将二进制代码反汇编生成MCInst, 最终进入到MCStreamer按照需要生成汇编文件。
LLVM MC的主要构成部分包括:
-
Instruction Encoder
-
Instruction Decoder
-
Assembly Parser
其中Instruction Encoder主要提供汇编接口,各个后端 (Target) 需要根据自己需求来实现具体的反汇编功能; Assembly Parser负责对汇编文件中的指令进行解析。
下面以LLVM的后端Cpu0为例介绍这三个组件。
2. LLVM MC 构成部分介绍
(1) Assembly Parser
Cpu0后端的Assembly Parser的作用是将汇编文件中的指令解析为LLVM MCInst,其由一个源文件'Cpu0AsmParser.cpp'组成,该文件包含从'MCTargetAsmParser'继承的‘Cpu0AsmParser’类:
public:
Cpu0AsmParser(const MCSubtargetInfo &sti, MCAsmParser &parser,
const MCInstrInfo &MII, const MCTargetOptions &Options)
: MCTargetAsmParser(Options, sti, MII), Parser(parser) {
setAvailableFeatures(ComputeAvailableFeatures(getSTI().getFeatureBits()));
}
MCAsmParser &getParser() const { return Parser; }
MCAsmLexer &getLexer() const { return Parser.getLexer(); }
在类声明中出现了以下两行,用于导入TableGen中与汇编的相关指令生成的函数。
#define GET_ASSEMBLER_HEADER
#include "Cpu0GenAsmMatcher.inc"
在'Cpu0AsmParser.cpp'中定义了Cpu0Operand类,包含了需要被解析的机器指令的操作数类型和内容信息。
class Cpu0Operand : public MCParsedAsmOperand {
enum KindTy {
k_Immediate,
k_Memory,
k_Register,
k_Token
} Kind;
(2) Instruction Encoder
Cpu0后端的Instruction encoder的作用是通过Cpu0MCCodeEmitter类将LLVM MCInst编码为二进制码。其具体实现位于'Cpu0MCCodeEmitter.cpp'中:
void Cpu0MCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
// Check for unimplemented opcodes.
unsigned Opcode = MI.getOpcode();
if ((Opcode != Cpu0::NOP) && !Binary)
llvm_unreachable("unimplemented opcode in encodeInstruction()");
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
uint64_t TSFlags = Desc.TSFlags;
// Pseudo instruction don't get encoded
// and shouldn't be here in the first place.
if ((TSFlags & Cpu0II::FrmMask) == Cpu0II::Pseudo)
llvm_unreachable("Pseudo opcode found in encodeInstruction()");
// For now all instruction are 4 bytes or 8 bytes
int Size = Desc.getSize(); // FIXME: Have Desc.getSize() return the correct value
EmitInstruction(Binary, Size, OS);
}
(3) Instruction Decoder
Cpu0后端的Instruction Decoder的作用是将一个抽象的字节序列转化成一个'MCInst'和一个'Size',其由一个源文件'Cpu0Disassembler.cpp'组成,扩展了'MCDisassembler'类,并以'getInstruction'函数为中心。此函数对字节序列进行解码,并将此信息储存在提供的'MCInst'中:
DecodeStatus Cpu0Disassembler::getInstruction(MCInst &Instr, uint64_t &Size,
ArrayRef<uint8_t> Bytes,
uint64_t Address,
raw_ostream &VStream,
raw_ostream &CStream) const {
DecodeStatus Result;
const unsigned MaxInstBytesNum = (std::min)((size_t)8, Bytes.size());
Bytes_ = Bytes.slice(0, MaxInstBytesNum);
do {
if (Bytes_.size() >= 8) {
uint64_t Insn;
Result = readInstruction64(Bytes_, Address, Size, Insn, IsBigEndian);
if (Result == MCDisassembler::Fail)
return Result;
// Calling the auto-generated decoder function
Result = decodeInstruction(DecoderTableCpu064, Instr, Insn, Address, this,
STI);
if (Result != MCDisassembler::Fail) {
Size = 8;
break;
}
}
if (Bytes_.size() < 4)
break;
Bytes_ = Bytes_.slice(0, 4);
uint32_t Insn;
Result = readInstruction32(Bytes_, Address, Size, Insn, IsBigEndian);
if (Result == MCDisassembler::Fail)
return Result;
// Calling the auto-generated decoder function
Result = decodeInstruction(DecoderTableCpu032, Instr, Insn, Address, this, STI);
if (Result != MCDisassembler::Fail) {
Size = 4;
break;
}
} while (false);
return Result;
}
'Cpu0Disassembler.cpp'提供了获取指令操作数类型、编码的寄存器值、以及内存中可以找到该指令的地址的函数。这些函数的名称由TableGen在构建解码表时定义但遵循'DecodeRegClassRegisterClass'的形式:
static DecodeStatus DecodeGPROutRegisterClass(MCInst &Inst,
unsigned RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeCPURegsRegisterClass(Inst, RegNo, Address, Decoder);
}
static DecodeStatus DecodeSRRegisterClass(MCInst &Inst,
unsigned RegNo,
uint64_t Address,
const void *Decoder) {
return DecodeCPURegsRegisterClass(Inst, RegNo, Address, Decoder);
}
static DecodeStatus DecodeSimm14(MCInst &Inst,
unsigned Insn,
uint64_t Address,
const void *Decoder) {
Inst.addOperand(MCOperand::createImm(SignExtend32<14>(Insn)));
return MCDisassembler::Success;
}
static DecodeStatus DecodeSimm32(MCInst &Inst,
unsigned Insn,
uint64_t Address,
const void *Decoder) {
Inst.addOperand(MCOperand::createImm(SignExtend32<32>(Insn)));
return MCDisassembler::Success;
}
3. 总结
LLVM MC 引入 MCInst 类,使得能够在合适位置添加指令描述 (instruction description),而同时获得汇编器 (assembler)、反汇编器 (disassembler) 和编译器后端支持 (compiler backend support)。对于新的LLVM后端如果要实现MC层的功能,需要添加与具体后端相关的AsmParser,以及实现与具体后端相关的反汇编功能。
本文针对 LLVM MC做了简单的介绍。受限于笔者知识水平,文中可能会存在某些理解身上的偏差,欢迎批评指正。
更多推荐
所有评论(0)