harmony-JIT

mac2022-06-30  75

目录 1.组成 1.1 translator 1.2 optimizer 1.3 Code Generator 2.IR 2.1 控制流图 3.JET 3.1 架构 3.2 处理流程 1.组成 ------ 1.1 translator -------------- 将复杂的bytecode翻译成HIR,HIR相比于字节码更加低级,它将复杂 的字节码拆分成多条简单语句,从而暴露出更多的优化机会。例如: load一个对象成员的字节码被拆分成一条断言对象引用不为空的语句、 一条load对象基地址的语句、计算成员地址的语句和load成员地址的 值的语句。 翻译过程分为两步: 1.建立基本块边界和异常处理区域,推导变量和运算符类型。在该 阶段translator使用和JVM说明书中描述的字节码验证算法相似的 方法生成变量的类型信息以及虚拟机的栈布局。 2.translator生成HIR并且做一些简单的优化:常量和拷贝传播、折 叠、去虚拟化、内联函数调用、冗余对象初始化检查去除以及 (value numbering-based redundancy elimination)。 1.2 optimizer ------------- 优化器采用了许多和字节码以及平台无关的优化技术。使用一系列经典 的面向对象优化技术以取得编译时间和优化效率的平衡。每一个high-level optimization表示成一个独立的“转换趟”,优化的目的包括: 1.域增强:扩展优化作用域 2.冗余移除:在不改变语义的前提下去除冗余操作 3.HIR简化:清理“趟”之间的中间表示 高层优化应用之后,代码选择器将HIR翻译成LIR。代码选择器被设计成 可插拔组建,一个具体的代码选择器必须实现各种方法结构体的回调处 理函数(例如:整个方法、基本块和指令),代码选择阶段,选择器使 用注册的回调接口将这些实体从HIR转换成LIR。 指令选择基于待编译方法的层次结构。对于一个方法、多重定义操作数 的集合、控制流图、控制流图的基本块节点,指令选择器框架做了如下 定义: 1.一个代表HIR指令选择器的抽象类以及一个默认实现,负责HIR的遍历 2.回调接口将分析实体从HIR转换到LIR。回调由代码生成器实现,负责 LIR的创建 框架的工作流程如下: 1.优化器创建一个代码选择器的实例用于HIR的遍历 2.代码生成器创建一个回调接口的实例用于LIR的创建 3.变量CS遍历所有的HIR操作数同时调用CS回调函数为这些操作数生成LIR 操作数;CFG CS遍历所有的HIR节点和边,同时调用CFG CS回调函数创 建相关的LIR节点和边;对于每一个基本块节点,CFG CS创建一个基本块 CS,并且进一步调用基本块CS回调函数将HIR指令转换成LIR(基本块CS 也常被称为指令选择器)。 1.3 Code Generator ------------------ 代码生成器负责将IR转换成机器码。代码生成器还会进行一些辅助操作,包括: 1.为代码中使用的常量创建数据区域 2.为运行时支持创建辅助数据结构,例如:栈布局描述、GC map和寄存器 3.注册异常处理函数 4.(Registration of direct calls for patching) 代码生成过程同具体的实现紧密相关。主要包括下面几步: 创建LIR 1.将绝大多数的二元操作数指令转换成三元操作数指令 2.每一个函数调用点用一条单一指令实现,去除创建被调用函数参数栈的指令 3.64字节整数操作表示成伪指令 4.地址算术精确化 LIR转换 1.在循环回边插入yield指令用于安全线程挂起 2.执行GC safe-point第一趟分析,确保代码生成结束后可以生成正确的GC map 3.如果需要,将地址计算折叠成复杂的地址形式 4.将64字节整数计算使用的伪指令扩展成真正的原生指令 5.将LIR指令从3地址形式转换成原生的2地址形式 6.分析指令和调用管理约束。代码生成器根据分析结果拆分操作数,以确保每个 操作数满足它所在的指令 7.全局寄存器分配,将最频繁使用的操作数分配到通用寄存器或XMM寄存器中 8.对每一个基本块做局部寄存器分配和溢出代码生成。该过程确保每一个操作数 都被赋予一个物理位置:寄存器或者栈。 9.基于profile信息将CFG基本块线性化 10.将拷贝伪寄存器指令扩展成真正的原生指令序列。寄存器操作数的拷贝如果没 有重叠的活动范围话,将它们合并在一起 11.检查栈帧为每一个栈操作数赋予一个偏移值创建栈帧布局描述信息 实际的代码生成器还会包含一些优化趟:常量和拷贝传播、死代码删除、冗余比较删 除。 代码数据发射 1.为LIR生成二进制块并连接到VM中。具体包括:创建常量数据区;将LIR链接到VM的 的数据结构和常量数据区中;将LIR翻译成机器码;注册对其他受管理代码的直接调 用;注册try块和相关的异常处理;注册内联方法的信息 2.更新栈帧布局信息,例如:栈深度限制 3.创建GC map描述每一个GC安全点的根对象位置 4.将栈帧布局描述信息、GC map和字节码写入编译方法的内存块中,这些将来会在运行 时用到,例如:栈帧布局描述信息用来为异常处理做栈反转、根对象枚举以及其他栈 迭代操作;GC map用于垃圾回收器进行根对象枚举;字节码用于映射原生指令和字节 码 全局锁 由于当前VM实现中的内存分配函数并非线程安全,Jitrino设置了一个全局锁用于确保在 代码生成阶段正确的为待编译方法分配内存。全局锁在多线程场景下必须加以慎重对待, 比如:多个线程同时开始编译同一个方法。全局锁在JET和OPT之间共享,以确保只有一个 线程可以正确的分配到内存。锁在lock_method Action对象中设置,在unlock_method Action对象中释放 lock_method Action还会检查当前的JIT实例是否为待编译方法分配了代码块。如果代码块 已经分配,那么说明该方法已经被其他线程进行了编译。这时,lock_method Action不会去 持有锁,而是停止编译并且返回状态码-COMPILATION_FINISHED 2.IR ---- JET没有IR,它直接将字节码编译成原生指令。OPT使用HIR和LIR。编译一个方法时,OPT将字节码 转换成一个具有节点、边和指令的图状结构。节点和边代码程序的控制流。图中的每个节点填充代 表原语操作的指令。 2.1 控制流图 ------------ HIR和LIR使用一个同一个基本数据结构表示编译方法的逻辑结构-CFG。这种统一使Jitrino避免的 内部代码的备份,减小代码体积提升生成代码的质量。当前的CFG实现位于jitrino/src/shared/ ControlFlowGraph.h和.cpp文件中。 CFG要实现的目标: 1.通过数据结构和函数创建控制流图:节点、边和指令 2.实现各种算法:节点排序、清除不可达代码和空节点 3.控制流图的可扩展性 CFG支持两种类型的控制流: 1.传统的控制流表示控制流的转移包括:jump指令和条件转移指令。传统的控制流由基本块节点 间的边表示 2.异常控制流表示抛出异常和接收异常造成的控制流转移。这个工作由分派节点刻画:抛出异常 由基本块节点到分派节点的边表示;接收异常由分派节点到基本块节点的边表示 由于IR可以表示异常控制流,优化器和代码生成器就可以将异常处理纳入优化范畴。比如局部的异 常处理可以由代价更小的直接转移来替换。 CFG结构-节点 CFG使用三种类型的节点: 1.Block节点代表常见的节点 2.Dispatch代表异常抛出路径 3.Exit代表方法退出(Exit节点是人为添加的一个节点,控制一个方法所有的异常和正常退出点, 一个图通常只有一个Exit节点) 除此以外,CFG还有一些专用的Block和Dispatch节点,Entry块节点标记一个方法的进入,并且拥 有一个图的所有节点。Return块节点控制所有正常退出的路径。Unwind分派节点控制所有的异常 抛出路径。Return和Unwind节点永远都有且只有一个出边指向Exit节点 CFG结构-边 一条边连结两个节点,边的类型在运行时由它所连接的两个节点计算而来,如下: 1.Dispatch边连结一个block或dispatch节点到另一个dispatch节点 2.Unconditional边连结两个block节点,描述一个无条件转移。从Return和Unwind到Exit节点的 边也被认为是无条件的 3.True和False边连结两个非空的block节点代表条件转移或分支。 4.Catch边连结dispatch和block节点。这些边代表异常的接收地点和用户代码的正常恢复地点。 CFG结构-指令 每一个节点维持了一个指令链表用以提供节点出边的类型信息。每当边信息当前无法确定时,CFG 从节点的最后一条指令获取边信息。 例子 Java代码 public static int max(int x, int y) { if (x > y) { return x; } return y; } Java字节码 public static int max(int, int): Code: 0: iload_0 1: iload_1 2: if_icmple 7 5: iload_0 6: ireturn 7: iload_1 8: ireturn 3.JET ----- 3.1 架构 -------- 代码生成阶段,模拟出方法操作数栈的状态。这个状态可以帮助计算GC map。GC map可以获知一个 局部变量或一个栈帧槽是否包含一个对象。栈帧的GC map在GC触发点(可能触发GC的事件)更新, 在调用之前编译阶段计算得到的栈帧深度和栈状态存储到方法栈中一块预先分配的内存区域中。这 个区域包含GC信息、包括操作数栈深度,栈帧GC map等。 3.2 处理流程 ------------ JET对字节码进行两趟处理,第一趟拆分基本块,第二趟生成代码。

转载于:https://www.cnblogs.com/Anney/archive/2013/02/05/2892460.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)