转载来自P2Tree
十分感谢翻译成果。

LLVM 目标无关代码生成器

  • 1 介绍:LLVM 目标无关代码生成器由 6 个主要的组件组成:
    • 1.1 代码生成
    • 1.2 代码生成器的高层设计
    • 1.3 使用 TableGen 来描述目标平台
  • 2 目标描述类
    • 2.1 TargetMachine 类
    • 2.2 DataLayout 类
    • 2.3 TargetLowering 类
    • 2.4 TargetRegisterInfo 类
    • 2.5 TargetInstrInfo 类
    • 2.6 TargetFrameLowering 类
    • 2.7 TargetSubtarget 类
    • 2.8 TargetJITInfo 类
  • 3 机器代码描述类
    • 3.1 MachineInstr 类
      • 3.1.1 使用 MachineInstrBuilder.h 函数
      • 3.1.2 固定寄存器(预分配寄存器)
      • 3.1.3 Call-clobbered 寄存器
      • 3.1.4 SSA 形式下的机器码
    • 3.2 MachineBasicBlock 类
    • 3.3 MachineFunction 类
    • 3.4 MachineInstr Bundles
  • 4 MC 层
    • 4.1 MCStreamer API
    • 4.2 MCContext 类
    • 4.3 MCSymbol 类
    • 4.4 MCSection 类
    • 4.5 MCInst 类
  • 5 目标无关的代码生成算法
    • 5.1 指令选择
      • 5.1.1 SelectionDAG 的介绍
      • 5.1.2 SelectionDAG 指令选择过程
      • 5.1.3 初始化 SelectionDAG 构造
      • 5.1.4 SelectionDAG 合法化类型阶段
      • 5.1.5 SelectionDAG 合法化操作阶段
      • 5.1.6 SelectionDAG 优化阶段:DAG Combiner
      • 5.1.7 SelectionDAG 指令选择阶段
      • 5.1.8 SelectionDAG 调度和规范化阶段
      • 5.1.9 SelectionDAG 的未来方向
    • 5.2 基于 SSA 的机器代码优化(原文未完成)
    • 5.3 活动区间(Live Intervals)
      • 5.3.1 活动变量分析
      • 5.3.2 活动区间分析
    • 5.4 寄存器分配
      • 5.4.1 LLVM 中如何表示寄存器
      • 5.4.2 将虚拟寄存器映射到物理寄存器
      • 5.4.3 处理双地址指令
      • 5.4.4 SSA 解构阶段
      • 5.4.5 指令折叠
      • 5.4.6 内建的寄存器分配器
    • 5.5 Prolog/Epilog 代码插入(原文未完成)
    • 5.6 Compact Unwind(未完成)
    • 5.7 后端 Machine Code 优化(原文未完成)
    • 5.8 代码发射
      • 5.8.1 发射函数栈信息
    • 5.9 VLIW 打包
      • 5.9.1 将指令映射到功能单元
      • 5.9.2 打包表如何生成和使用
  • 6 实现一个原生汇编器
    • 6.1 指令解析(原文未完成)
    • 6.2 指令别名分析
      • 6.2.1 助记符别名
      • 6.2.2 指令别名
    • 6.3 指令匹配(原文未完成)
  • 第七章是目标相关的一些实现细节。

1 介绍:LLVM 目标无关代码生成器由 6 个主要的组件组成:

抽象的目标描述接口(include/llvm/Target/):用来维护那些重要的机器特性,而不需要考虑他们的使用。

用来做代码生成的类(include/llvm/CodeGen/):这些类是通用的,抽象地囊括了所有的后端机器,他们用来做代码生成工作。

用来描述目标文件的类和算法:在 MC 层下,这些类描述了汇编级别的概念,如 labels、 sections、 instructions。

目标无关的算法(lib/CodeGen/ ):提供了一些基础的后端算法,如寄存器分配、调度、栈帧表示等工作的算法。

抽象目标描述接口的实现(lib/Target/):针对特定后端目标,这部分代码实现了 LLVM 提供的抽象描述接口,并且可以根据需要自定义一些目标相关的 pass,从而来实现针对特定目标的代码生成器。

JIT 组件(lib/ExecutionEngine/JIT/):完全与目标无关的工具,实现 JIT 的功能。

1.1 代码生成

LLVM 代码生成器的两个最重要的组成部分分别是:代码生成器的高层接口、一系列可重用与构建目标相关后端的组件。有两个很重要的接口:TargetMachine 和 DataLayout.

1.2 代码生成器的高层设计

指令选择:会将 LLVM 的代码转换为目标相关的 DAG (有向无环图)。

指令调度和规范化:这个阶段对目标相关的 DAG 进行处理,对指令顺序进行调整,然后依据这种顺序发射一种叫做 MachineInstrs 的代码表示。

基于 SSA 的机器代码优化:这是一个可选的阶段,由一系列针对 SSA 结构的机器代码优化操作组成。

寄存器分配:将 SSA 结构下带有无限虚拟寄存器的机器代码转换为有限机器寄存器的结构。

Prolog/Epilog 代码插入:当需要生成带有函数的机器代码时,prolog 和 epilog 代码将会被插入到逻辑代码之间,并且抽象的栈引用会被消除。

机器代码优化:对“最终”的机器代码进行一次后优化

代码发射:真正的最后阶段,将输出适配对应机器汇编格式的汇编码或可执行的二进制机器码。

1.3 使用 TableGen 来描述目标平台

越来越多的目标描述性信息放到 .td 文件(TableGen 处理文件)中完成。

2 目标描述类

LLVM 目标描述的类提供了一套抽象的针对任意后端机器通用的描述。这些类被设计来管理一些抽象的目标属性(比如指令和寄存器),并且不夹杂任何代码生成算法的片段。

除了 DataLayout 以外,其他所有的目标描述类都需要作为基类被特定目标的描述类所继承,同时提供虚函数的实现。为了便于实现, TargetMachine 类提供了一些访问器(accessor),这些也同样需要在特定目标的描述类中实现。

2.1 TargetMachine 类

TargetMachine 类提供了一些虚函数用来访问与特定目标相关的访问器,被命名为 get*Info 方法,比如 getInstrInfo、getRegisterInfo、getFrameInfo 等。这个类需要被特定目标的类所继承,比如 X86 中继承 TargetMachine 实现的 X86TargetMachine,其中需要实现各种虚函数。唯一必须依赖的类是 DataLayout,但是如果其他功能类也被使用的话,它们同样也需要被实现。

2.2 DataLayout 类

DataLayout 类是唯一必须依赖的目标描述类,而且不能被继承。 DataLayout 指定了一些与目标 layout 有关的信息,比如内存结构,各种数据类型的对齐要求,指针占用空间,大小端的要求。

2.3 TargetLowering 类

TargetLowering 类被 SelectionDAG 组件所使用,在指令选择器中发挥功能,它描述了 LLVM IR 代码如何 lowering 到 SelectionDAG 结构。

2.4 TargetRegisterInfo 类

TargetRegisterInfo 类用于描述与目标相关的寄存器信息以及任何与寄存器的交互动作。

2.5 TargetInstrInfo 类

TargetInstrInfo 类被用于描述特定目标支持的机器指令码。会描述指定对应的助记符和操作码、操作数的数量、隐式寄存器 uses 和 defs、一些目标无关的属性(比如指令是否访问内存、是否可交换)、以及维护目标平台的 flag。

2.6 TargetFrameLowering 类

TargetFrameLowering 类被用于提供一些目标栈帧相关的信息。比如栈的生长方向,进入函数的栈对齐要求和对局部数据的偏移。对局部数据的偏移是针对进入函数的栈指针的偏移,这里还存储一些处理函数的数据,如局部变量、 spill 位置等。

2.7 TargetSubtarget 类

TargetSubtarget 类被用于提供一些与目标特定芯片相关的信息。

2.8 TargetJITInfo 类

TargetJITInfo 类提供了一个抽象的接口,用于支持特定目标的 Just-In-Time 代码生成器的工作

3 机器代码描述类

LLVM 代码会翻译成由 MachineFunction、MachineBasicBlock 和 MachineInstr 实例组成的特定机器的表示。这种表示完全是目标无关的,以最抽象的方式描述指令:操作码和操作数。

3.1 MachineInstr 类

目标机器指令(Machine Instruction,LLVM 后端的一种中间表示,缩写为MI )通过 MachineInstr 的实例来描述。

操作码是一个简单的无符号整型数,只在特定后端下才有效。所有的指令都是在 *InstrInfo.td 文件中定义的,操作码的枚举值仅仅是依据这份描述自动生成。MachineInstr 类没有任何有关于指令意义的信息(比如这条指令的意义是什么),必须依赖于 TargetInstrInfo 类来了解。

操作数可以有多种不同类型,比如寄存器引用、常量值、basic block 引用等。另外,操作数应该被标记为 def 或 use (仅寄存器可以标记为 def )。

3.1.1 使用 MachineInstrBuilder.h 函数

机器指令(MI)由 BuildMI 函数来创建,这个函数定义在 include/llvm/CodeGen/MachineInstrBuilder.h 文件中。BuildMI 函数可以很容易的创建任意 MI。

3.1.2 固定寄存器(预分配寄存器)

代码生成器需要注意那些固定寄存器。特别是在指令流中,寄存器分配器必须针对一些指令将特定的寄存器安排到特定的位置上。这可能发生于一些有限制的指令(比如,X86 中只允许 EAX / EDX 这两个寄存器完成 32 位除法操作)或者一些扩展功能,如调用约定。任何情况下,指令选择器都应该在指令需要时,发出将虚拟寄存器传入或传出物理寄存器的操作代码。

比如,在如下这段 LLVM 代码中:

define i32 @test(i32 %X, i32 %Y) {
%Z = sdiv i32 %X, %Y
ret i32 %Z
}
X86 的指令选择器可能会生成以下代码:

;; Start of div
%EAX = mov %reg1024 ;; Copy X (in reg1024) into EAX
%reg1027 = sar %reg1024, 31 ;; Sign extend X
%EDX = mov %reg1027 ;; Sign extend X into EDX
idiv %reg1025 ;; Divide by Y (in reg1025)
%reg1026 = mov %EAX ;; Read the result (Z) out of EAX

;; Start of ret
%EAX = mov %reg1026 ;; 32-bit return value goes in EAX
ret
寄存器分配器会合并寄存器,并删除冗余寄存器并产生以下代码:

;; X is in EAX, Y is in ECX
mov %EAX, %EDX
sar %EDX, 31
idiv %ECX
ret
这种操作非常通用,它允许任何目标平台实现这种将指令流中信息在指令选择器中独立出来的操作。需要注意对于物理寄存器来说,应当尽可能缩短其生命周期,并且,在进入和退出一个 basic blocks 时(寄存器分配前),所有物理寄存器都会被假设是 dead 状态。因此,如果你需要一个能跨越 basic block 边界的值,你应该使用虚拟寄存器。

3.1.3 Call-clobbered 寄存器

3.1.4 SSA 形式下的机器码

MI 代码在指令选择时被创建为 SSA 形式,并直到寄存器分配发生。大多数情况下,这种形式都是非常简单的,因为 LLVM 代码也是 SSA 形式,LLVM 的 PHI 节点会被转换为 MI 代码的 PHI 节点,虚拟寄存器也只需要通过简单的方法来描述。

在寄存器分配之后,MI 代码不再是 SSA 形式,因为虚拟寄存器不存在了。

3.2 MachineBasicBlock 类

MachineBasicBlock 类包含有一系列的 MI ( MachineInstr 的实例 ),它大致等效于输入到指令选择器的 LLVM 代码,但也可能存在一对多的映射(一个 LLVM basic block 对应多个 machine basic block )。MachineBasicBlock 类中有个 getBasicBlock 方法,可以返回对应的 LLVM basic block。

3.3 MachineFunction 类

MachineFunction 类包含有一系列的 machine basic block ( MachineBasicBlock 的实例 ),它与 输入指令选择器的 LLVM 函数是一对一的映射。除了一系列的 machine basic block 以外,它还包括一个 MachineConstantPool,一个 MachineFrameInfo,一个 MachineFunctionInfo,和一个 MachineRegisterInfo。这部分代码可参见 include/llvm/CodeGen/MachineFunction.h。

3.4 MachineInstr Bundles

LLVM 代码生成器可以将一些指令序列看做是 MachineInstr bundles。 一个 MI bundle 可以模型化 VLIW 中包含能够并行化执行的 组/包 结构。它也可以被用来模型化无法合法分离的顺序指令序列(可能带有数据依赖)。

概念上讲,一个 MI bundle 就是将一个 MI 和其他一些 MIs 打包在一起:
Bundle —
|
| MI
| |
| MI
| |
| MI
|
Bundle —
|
… …
不同后端可能会在这里有不同的实现,但在 LLVM 看来,一个 bundle 会被当做一个 MI 一样的行为来处理。

4 MC 层

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

4.1 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 子对象。

4.2 MCContext 类

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

4.3 MCSymbol 类

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

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

foo:
bar:
.byte 4
我们看到,foo 和 bar 这两个 label 是在同一个目标文件地址的(但他们是不同符号)。

4.4 MCSection 类

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

4.5 MCInst 类

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

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

5 目标无关的代码生成算法

5.1 指令选择

指令选择是 LLVM 代码生成中的一个重要步骤,它将高层的 LLVM 代码表示翻译为目标相关的机器指令。在学术界有多种不同的实现方法,LLVM 使用了基于 SelectionDAG 的指令选择器。

DAG 指令选择器的大部分内容是通过 tablegen 依赖 .td 文件生成的。我们的目标便是将这些 .td 文件生成一个完整的指令选择器,不过,当前仍然有一些工作必须依赖于 C++ 来完成。

5.1.1 SelectionDAG 的介绍

SelectionDAG 提供了一种抽象的代码表示方式来通过自动化技术完成指令选择构建(比如基于动态编程的最优模式匹配选择器,dynamic-programming based optimal pattern matching selectors)。

SelectionDAG 是一个有向无环图(Directed-Acyclic-Graph),其中的节点由 SDNode 类来定义。SDNode 最主要的内容是操作码(Opcode),操作码表示节点的一种操作。在 include/llvm/CodeGen/ISDOpcodes.h 文件中定义了很多节点操作码类型。

SelectionDAG 有两种不同的值类型:表示数据流和表示控制流依赖。数据流的值是简单的边,它们的类型是整形或浮点型。控制流的值用来表示一个链(chain),它们的类型是 MVT::Other。这些边(控制流的边)提供了约束拥有 side-effect 的节点的顺序(比如 load、store、call、return 等)。所有拥有 side-effect 的节点都应该有一个链的边(token chain)作为输入,并输出到一个新的节点。约定 token chain 的输入总是 0,输出总是一个操作的最后一个结果。不过,在指令选择之后(译注:之后全部为 machine node),machine node 在指令操作数最后还会有一个 chain,可能跟随有一个 glue 节点。

一个 SelectionDAG 会有一个 Entry 和一个 Root 节点。Entry 节点用来标记入口节点,其操作码是 ISD::EntryToken。Root 节点是最后一个节点(译注:最后的最后,side-effect 的最后),比如一个 basic block 的 return 节点。

一个非常重要的概念是合法 DAG 和不合法 DAG。一个合法的 DAG 是指只使用目标机器支持的操作数和类型构建的 DAG。比如,在 32 位 PowerPC 上,拥有 i1,i8,i16,i64 值类型的 DAG 是不合法的,使用 SREM 或 UREM 操作的 DAG 也是不合法的。合法化类型和合法化操作阶段负责将一个非法的 DAG 逐步转变为一个合法的 DAG。

5.1.2 SelectionDAG 指令选择过程

SelectionDAG 的指令选择分为以下几个步骤:

(1)构造初始化的 DAG:这个阶段将输入的 LLVM IR 翻译为一个非法的 SelectionDAG;
(2)优化 SelectionDAG:这个阶段完成一些在 SelectionDAG 上的简单优化,试图简化它,并识别一些目标机器支持的元指令(比如 rotate 和 div/rem 组合)。这样可以让代码更加高效,使后边的工作简单一些;
(3)合法化 SelectionDAG 类型:这个阶段消除 SelectionDAG 中那些目标平台不支持的类型;
(4)优化 SelectionDAG:类型合法化后,再次做一些优化,清理由前者产生的冗余(译注:也被称为 combine 操作);
(5)合法化 SelectionDAG 操作:这个阶段消除 SelectionDAG 中那些目标平台不支持的操作;
(6)优化 SelectionDAG:同理,再做一些优化,清理由前者产生的低效的实现 (译注:也被称为 combine 操作);
(7)通过 DAG 做指令选择:最后,目标指令选择器将 DAG 中的操作匹配为目标指令。这个阶段将目标无关的指令全部翻译为目标相关的指令;
(8)SelectionDAG 指令调度和规范化:最后一步,为 DAG 形式下的目标指令做线性顺序调度,并将它们发射到 MachineFunction 结构中。这一步骤中用到了传统的调度技术。

在所有这些步骤结束后,SelectionDAG 结构就会被销毁,并继续后续的步骤。

有一种可视化的技巧,可以通过一些简短的 llc 命令行参数来检查 DAG 在不同阶段的形式。

5.1.3 初始化 SelectionDAG 构造

初始化 SelectionDAG 是通过 SelectionDAGBuilder 类将 LLVM 类直接通过窥孔展开。这一阶段的目的就是在 SelectionDAG 中暴露尽可能多的低层次的、目标相关的细节。这一部分大多数代码都是手动编码的(比如 LLVM 的 add 指令会翻译为一个 SDNode 的 add 节点,而 getelementptr 指令会被展开成多条算术指令)。这个阶段需要一些目标相关的 hooks(译注:俗称钩子函数)来下降 call、return、varargs 等。TargetLowering 接口用来完成这些工作。

5.1.4 SelectionDAG 合法化类型阶段

具体参考

5.1.5 SelectionDAG 合法化操作阶段

5.1.6 SelectionDAG 优化阶段:DAG Combiner

5.1.7 SelectionDAG 指令选择阶段

5.1.8 SelectionDAG 调度和规范化阶段

5.1.9 SelectionDAG 的未来方向

5.2 基于 SSA 的机器代码优化(原文未完成)

5.3 活动区间(Live Intervals)

5.3.1 活动变量分析

5.3.2 活动区间分析

5.4 寄存器分配

5.4.1 LLVM 中如何表示寄存器

5.4.2 将虚拟寄存器映射到物理寄存器

5.4.3 处理双地址指令

5.4.4 SSA 解构阶段

5.4.5 指令折叠

5.4.6 内建的寄存器分配器

5.5 Prolog/Epilog 代码插入(原文未完成)

5.6 Compact Unwind(未完成)

5.7 后端 Machine Code 优化(原文未完成)

5.8 代码发射

5.8.1 发射函数栈信息

5.9 VLIW 打包

5.9.1 将指令映射到功能单元

5.9.2 打包表如何生成和使用

6 实现一个原生汇编器

6.1 指令解析(原文未完成)

6.2 指令别名分析

指令解析之后,会进入到 MatchInstructionImpl 函数。该函数实现了别名处理,之后会做真正的匹配工作。

别名分析是为了识别同样一条指令的不同文本形式,并下降到同一种指令表示。在 TableGen 中有几种不同类型的别名描述,将会在后续小节阐述,它们的顺序是从简单到复杂排好序的,一般情况下,只需要使用第一种描述方法即可,这通常是最简洁的描述方法。

6.2.1 助记符别名

助记符别名的描述比较简单,它是一个 TableGen 的类 MnemonicAlias,接受两个指令类。它将一个输入的助记符映射到一个输出的助记符。

6.2.2 指令别名

通常对指令别名的解析是在指令匹配过程时完成的,需要提供新的别名映射关系和特定要生成的指令。所以,一个指令别名的描述分为两部分:要匹配的指令字符串名以及要生成的指令。比如:

def : InstAlias<"movsx $src, dst",(MOVSX16rr8WGR16:dst", (MOVSX16rr8W GR16:dst",(MOVSX16rr8WGR16:dst, GR8 :$src)>;
def : InstAlias<"movsx $src, dst",(MOVSX16rm8WGR16:dst", (MOVSX16rm8W GR16:dst",(MOVSX16rm8WGR16:dst, i8mem:$src)>;
def : InstAlias<"movsx $src, dst",(MOVSX32rr8GR32:dst", (MOVSX32rr8 GR32:dst",(MOVSX32rr8GR32:dst, GR8 :$src)>;
def : InstAlias<"movsx $src, dst",(MOVSX32rr16GR32:dst", (MOVSX32rr16 GR32:dst",(MOVSX32rr16GR32:dst, GR16 :$src)>;
def : InstAlias<"movsx $src, dst",(MOVSX64rr8GR64:dst", (MOVSX64rr8 GR64:dst",(MOVSX64rr8GR64:dst, GR8 :$src)>;
def : InstAlias<"movsx $src, dst",(MOVSX64rr16GR64:dst", (MOVSX64rr16 GR64:dst",(MOVSX64rr16GR64:dst, GR16 :$src)>;
def : InstAlias<"movsx $src, dst",(MOVSX64rr32GR64:dst", (MOVSX64rr32 GR64:dst",(MOVSX64rr32GR64:dst, GR32 :$src)>;
这个例子成功的将相同的指令别名映射为不同的指令(仅依赖于操作数在汇编上的类型)。对应生成的指令可以与指令字符串名中的操作数顺序不同,也可以多次使用字符串名中的输入操作数,比如:

def : InstAlias<"clrb reg",(XOR8rrGR8:reg", (XOR8rr GR8 :reg",(XOR8rrGR8:reg, GR8 :$reg)>;
def : InstAlias<"clrw reg",(XOR16rrGR16:reg", (XOR16rr GR16:reg",(XOR16rrGR16:reg, GR16:$reg)>;
def : InstAlias<"clrl reg",(XOR32rrGR32:reg", (XOR32rr GR32:reg",(XOR32rrGR32:reg, GR32:$reg)>;
def : InstAlias<"clrq reg",(XOR64rrGR64:reg", (XOR64rr GR64:reg",(XOR64rrGR64:reg, GR64:$reg)>;
这个例子展示了多次使用在字符串名中的同一个操作数。在 X86 后端中,XOR8rr 有两个输入的 GR8 寄存器和一个输出的 GR8 寄存器(其中一个输入和输出共用同一个寄存器)。InstAlias 的操作数是直接对应的,不需要特殊指定重复的寄存器。生成的指令还可以直接使用立即数或固定寄存器作为操作数,如:

// 固定的立即数操作数
def : InstAlias<“aad”, (AAD8i8 10)>;

// 固定的寄存器
def : InstAlias<“fcomi”, (COM_FIr ST1)>;

// 简单的别名描述
def : InstAlias<"fcomi reg",(COMFIrRST:reg", (COM_FIr RST:reg",(COMF​IrRST:reg)>;
指令别名的描述可以使用 Requires 来约束它针对特殊 subtarget 的所属性。

如果后端支持,指令输出器可以自动的发射指令别名,而不是指令别名的映射名,这样可以让输出的汇编代码更清晰可读。如果希望这样,在 InstAlias 中的第三个参数传入 0。

6.3 指令匹配(原文未完成)

原文待完成状态

第七章是目标相关的一些实现细节。

这一部分和硬件特性相关,暂时不太关心,就不翻译了。需要提示的是,这部分内容很可能不再更新,其中部分内容我发现已经与代码不符,真实情况要结合代码熟悉。

LLVM 目标无关代码生成器相关推荐

  1. LLVM文档目录框架(索引用)

    注1:主要用于个人学习LLVM查找用,后续会继续更新 注2:加红文字可以ctrl+鼠标点击跳转到对应官方文档 注3:目前大致目录框架如下 概述 LLVM 编译器简介演示文稿 LLVM 简介 LLVM: ...

  2. 无关于目标or特定于目标:简单且有效的零样本立场检测对比学习方法

    ©PaperWeekly 原创 · 作者 | 梁斌.陈子潇 单位 | 哈尔滨工业大学(深圳) 研究方向 | 情感分析.立场检测 内容简介 零样本立场检测(zero-shot stance detect ...

  3. LLVM语法语义指令特性

    LLVM语法语义指令特性 High Level Structure Module Structure LLVM 程序由Module's组成,每个 's 是输入程序的一个翻译单元.每个模块由函数,全局变 ...

  4. LLVM一些编程语法语义特性

    LLVM一些编程语法语义特性 High Level Structure Module Structure LLVM 程序由Module's组成,每个 's 是输入程序的一个翻译单元.每个模块由函数.全 ...

  5. LLVM一些语法规则

    LLVM一些语法规则 LLVM文档 LLVM编译器基础架构支持广泛的项目,从工业强度编译器到专门的JIT应用程序,再到小型研究项目. 同样,文档分为几个针对不同受众的高级别分组: LLVM设计概述 几 ...

  6. LLVM Backend技术

    LLVM Backend技术 LLVM编译器介绍 LLVM 是一个编译器基础设施的设计与实现,具有独特的多阶段优化系统.广泛支持过程间和配置文件驱动的优化,同时对于商业编译器系统也十分高效. LLVM ...

  7. The LLVM Compiler Infrastructure | LLVM编译器基础设施

    https://llvm.org/ LLVM项目是模块化和可重用的编译器及工具链技术的集合.尽管名称如此,LLVM与传统虚拟机关系不大.名称" LLVM"本身不是缩写.它是项目的全 ...

  8. LLVM中指令的一生

    原作:http://eli.thegreenplace.net/2012/11/24/life-of-an-instruction-in-llvm/ 作者:Eli Bendersky LLVM是一个复 ...

  9. 经典编译器组成(前端+优化器+后端)以及LLVM和Clang简介

    目录 1,典型的编译器结构:前端+优化器+后端 2,LLVM 简介 3,Clang简介 1,典型的编译器结构:前端+优化器+后端 一个传统的静态编译器(比如C 编译器)最普遍的设计是分为三个部分,如下 ...

最新文章

  1. python模块的分类有哪些_python之模块分类(六)
  2. 看了这一篇,就不用看别的——Java中Object关于锁的的三个方法:wait,notify,notifyAll的作用
  3. ofdma技术_SC-FDMA技术
  4. 软件工程师关注的播客
  5. BUUCTF crackMe 题解
  6. JavaWeb课程设计学生宿舍管理系统
  7. 植物大战僵尸辅助C/C++版
  8. LINTCODE——吹气球
  9. prisma2.0和nexus搭建graphql后端(1)—prisma2.0
  10. 服务器physx性能测试,望穿秋水! PhysX卡性能测试首度曝光
  11. java web 图片 加载_Java web开发中加载图片路径的两种方式
  12. 搭建风控系统道路上踩过的坑合集
  13. 编写java百分制转化五分制_百分制与五分制的换算(百分制转换五分制公式)
  14. HDLM命令dlnkmgr详解之三__view
  15. vs2008配置opencv2.4.0
  16. 用css画五边形(代码)
  17. 做多重线性回归 方差不齐怎么办
  18. 大数据开发【3】 -- hadoop的云集群配置
  19. 微信小程序导入demon
  20. 【web前端---阿里巴巴矢量图图库图标引用步骤 】

热门文章

  1. BUUCTF | Misc 二维码 -- BUUOJ WriteUP
  2. 无线网卡驱动突然坏了怎么办
  3. 政考网:公务员备考需要多久?
  4. Codeforces1509.B. TMT Document
  5. Chinaren校友录所用的左边弹出式菜单
  6. 关于Qt6.2中的所有属性绑定
  7. 关于功放的正确调整及安全匹配音箱方法的计算及说明
  8. c语言基础学习(2)
  9. The following assertion was thrown building LayoutDemo(dirty): A non-null String must be provided to
  10. 在线批量无损压缩图片工具