大多数程序员都懂的java虚拟机:C1编译器从字节码到HIR
从字节码到HIR
正如之前看到的,C1的HIR是一个基于静态单赋值的图IR,由基本块构成控制流图,由静态单赋值指令构成基本块,如图8-1所示。
所有的指令都派生自Instruction类,其中,BlockBegin表示基本块的起点,BlockEnd表示基本块的结束。BlockBegin和BlockEnd合起来表示一个基本块,BlockBegin的predecessors表示当前基本块的前驱块,BlockEnd的successors表示当前基本块的后继块,它们连接起来组成一幅控制流图。BlockBegin的next指向基本块的下一条指令,如LogicOp、LoadField等;下一条指令的next又指向再下一条,如此反复,最终形成一个指令序列,即基本块内部的SSA指令链表。
build_hir()会创建一个GraphBuilder对象,而这个创建的过程就是字节码转换为HIR的过程。该过程主要分为两步:首先使用BlockListBuilder划分出所有基本块,找出循环头,然后使用SSA指令(即Instruction的子类)填充每个基本块。
识别基本块
字节码是线性结构,所以在识别时可以使用BlockListBuilder线性地遍历字节码,找到if_cmp*、goto、throw、return、tableswitch、lookupswitch这些可以改变控制流的字节码,将它们标记为leader字节码,并据此划分出基本块的边界,如代码清单8-8所示。
代码清单8-8 划分基本块,找出循环头
BlockListBuilder::BlockListBuilder(...){
set_entries(osr_bci); // 设置入口基本块,会特殊处理OSR入口点
set_leaders(); // 找到leader字节码,划分基本块
mark_loops(); // 标记循环
}BlockListBuilder
还会标记循环,这个过程也是必要的,因为循环头所在的基本块可能存在多个前驱基本块,而多个前驱基本块隐含着一个变量可能会有不同的定义,所以为了合并同一个变量可能存在的不同定义,编译器需要创建Phi节点。编译器也包含了许多与循环相关的优化,它们都要求知晓循环所在。
抽象解释
当划分出基本块和找出循环头后,控制流图(CFG)已经初具雏形,但是基本块内部还是空的,换句话说,BlockBegin的next是空的,需要使用Instruction填充基本块。填充代码如代码清单8-9所示。
代码清单8-9 填充基本块
GraphBuilder::GraphBuilder(...){
// 划分基本块,找出循环头
BlockListBuilder blm(compilation, scope, osr_bci);
...// 设置控制流图入口基本块状态
_initial_state = state_at_entry();
start_block->merge(_initial_state);
switch (scope->method()->intrinsic_id()) {
... // 特殊处理一些intrinsic方法
default:
scope_data()->add_to_work_list(start_block);// 对于每个基本块,遍历字节码,解释得到SSA指令并填充基本块
iterate_all_blocks();
break;
}
...
}
由于CFG是图结构,C1将使用广度优先遍历,而广度优先遍历的实现通常需要用一个队列进行辅助,该队列即代码清单8-9所示的worklist。iterate_all_blocks()将使用广度优先遍历对每个基本块进行遍历,并对每个基本块的字节码抽象解释(Abstract Interpretation)。
所谓抽象解释是指C1像模板解释器一样,解释执行基本块对应的字节码,并生成对应的SSA指令。解释过程中需要的局部变量和操作数会放到ValueStack,如图8-2所示。
以图8-2所示为例,假设图中所示是一个基本块,包含了左边的字节码。C1解释执行字节码,并将状态放到ValueStack中。状态包括存放局部变量与函数入参的local和存放临时计算结果的stack。左侧的[i7,i8]表示局部变量,当解释iload_1时,加载局部变量i8到ValueStack中,该字节码不生成SSA指令;当解释iload_0时,加载i7到ValueStack,该字节码不生成SSA指令;当解释imul时,该字节码会生成SSA指令,该指令以ValueStack的两个值作为参数,产出新的值i11并放入ValueStack;当解释istore_1时,将i11放入局部变量表的第二个槽,该字节码不产生SSA指令。
解释完成后生成的三条SSA指令会填充到基本块中,至此HIR的构造就完成了,之前基于栈的字节码变成了基于寄存器的SSA指令。如果读者构造HotSpot VM时使用的是fastdebug类型,加上-XX:+PrintIR参数可以输出每一个步骤的HIR(这一步对应输出的IR after parsing阶段)。
注意,C1生成SSA指令后并非简单地加入基本块,而是会调用append_with_bci函数,该函数会对当前生成的SSA指令进行若干局部优化,如常量折叠、局部值编号等。换句话说,这些(由于SSA本身的特性决定)轻量级的优化在HIR构造完成时就已经完成了,而build_hir()实现的一些HIR优化是更为复杂,也相对重量级的优化。上面提到的这些轻量级优化的内容将在下节描述。
本文给大家讲解的内容是深入解析java虚拟机:C1编译器,从字节码到HIR
- 下篇文章给大家讲解的是深入解析java虚拟机:C1编译器,HIR代码优化;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
大多数程序员都懂的java虚拟机:C1编译器从字节码到HIR相关推荐
- 【Java 虚拟机原理】Class 字节码二进制文件分析 六 ( 属性类型 | Code 属性 | 属性名称索引 | 属性长度 | 操作数栈最大深度 | 局部变量存储空间 | 字节码长度 )
文章目录 前言 一.属性类型 二.Code 属性表数据结构 三.属性名称索引 四.属性长度 五.操作数栈最大深度 六.局部变量存储空间 七.字节码长度 八.存储字节码指令的一系列字节流 前言 上一篇博 ...
- 【Java 虚拟机原理】Class 字节码二进制文件分析 五 ( 方法计数器 | 方法表 | 访问标志 | 方法名称索引 | 方法返回值类型 | 方法属性数量 | 方法属性表 )
文章目录 前言 一.方法表结构 二.方法计数器 三.方法表数据解析 ( init 构造方法 ) 1.方法访问标志 2.方法名称索引 3.方法返回类型 4.方法属性数量 前言 上一篇博客 [Java 虚 ...
- 【Java 虚拟机原理】Class 字节码二进制文件分析 三 ( 访问和修饰标志 | 类索引 | 父类索引 | 接口计数器 | 接口表 | 字段计数器 | 字段表 )
文章目录 前言 一.访问和修饰标志 二.类索引 三.父类索引 四.接口计数器 五.接口表 六.字段计数器 七.字段表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( ...
- 【Java 虚拟机原理】Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | info[] | 完整分析字节码文件中的常量池二进制数据 )
文章目录 前言 一.常量池结构分析 1.常量池位置 2.常量池结构 3.常量池单个常量 4.常量池单个常量 tag 标签 二.常量池字节码文件分析 0.常量池附加信息 1.常量池 #1 常量分析 2. ...
- 【Java 虚拟机原理】Class 字节码二进制文件分析 七 ( 局部变量表分析 )
文章目录 前言 一.编译生成带局部变量表的字节码文件 二.局部变量表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | i ...
- 【Java 虚拟机原理】Class 字节码二进制文件分析 四 ( 字段表数据结构 | 字段表详细分析 | 访问标志 | 字段名称 | 字段描述符 | 属性项目 )
文章目录 前言 一.字段表总数据结构 二.访问标志 三.字段名称 四.字段描述符 五.属性项目数 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 三 ( 访问和修饰标志 | ...
- 【Java 虚拟机原理】动态字节码技术 | Dalvik ART 虚拟机 | Android 字节码打包过程
文章目录 一.动态字节码技术 二.Dalvik & ART 虚拟机 三.Android 字节码打包过程 总结 一.动态字节码技术 动态字节码技术 就是在 运行时 , 动态修改 Class 字节 ...
- 大神程序员都懂英文翻译,而你却因英语不行遭拒?
程序员中很多人都是在外企里面工作的.这样的话就会涉及到,跟外籍同事的沟通和协作.上下级之间的汇报工作.虽然我们的主要工作是开发软件.是技术类工作.跟计算机打交道比较多,但是沟通对技术来说,还是非常重要 ...
- 程序员都懂的30个段子,你知道几个?
1. 问:程序猿最讨厌康熙的哪个儿子?. A.皇长子允禔 C.皇八子允禩 B.皇三子允祉 D.皇十六子允禄 答案C皇八子胤禩.因为他是八阿哥(bug) 2. 程序猿要了3个孩子,分别取名叫Ctrl.A ...
最新文章
- java optional 用法_Java8 中的 Optional 相关用法
- 那些实用与颜值齐飞的桌面!
- Queue)) 类,msdn上的一篇文章,便于查看
- 51nod 1172 Partial Sums V2 卡精度的任意模数FFT
- Spring源码版本命名规则及下载安装(下)笔记
- 工业路由器和工业交换机的区别介绍
- Java底层抛出异常_总结java的exception
- 使用Java编一个有意思的_一个有趣的Java编译问题
- Java算法——回溯法
- GMM 简介与 Stata 实现
- 电脑ping,电脑ping怎么用|电脑ping怎么打开_PC6教学
- 【工业视觉】Halcon常用教程
- 雷达探测项目仿真代码(Matlab代码实现)
- pgsql 筛选中文字符正则_PostGreSql 判断字符串中是否有中文的案例
- 现代OpenGL教程 02——贴图
- Web 智能代码编辑器 WeBuilder 2022
- iPhone 13系列电池信息曝光,续航稳了
- tts代表_有什么好的文字转语音(TTS)的软件推荐?
- 2023五一加班工资三倍怎么算(附详细计算公式)
- 【自动驾驶-3D目标检测】3DSSD:基于点的3D单级目标检测器