从字节码到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

  1. 下篇文章给大家讲解的是深入解析java虚拟机:C1编译器,HIR代码优化;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

大多数程序员都懂的java虚拟机:C1编译器从字节码到HIR相关推荐

  1. 【Java 虚拟机原理】Class 字节码二进制文件分析 六 ( 属性类型 | Code 属性 | 属性名称索引 | 属性长度 | 操作数栈最大深度 | 局部变量存储空间 | 字节码长度 )

    文章目录 前言 一.属性类型 二.Code 属性表数据结构 三.属性名称索引 四.属性长度 五.操作数栈最大深度 六.局部变量存储空间 七.字节码长度 八.存储字节码指令的一系列字节流 前言 上一篇博 ...

  2. 【Java 虚拟机原理】Class 字节码二进制文件分析 五 ( 方法计数器 | 方法表 | 访问标志 | 方法名称索引 | 方法返回值类型 | 方法属性数量 | 方法属性表 )

    文章目录 前言 一.方法表结构 二.方法计数器 三.方法表数据解析 ( init 构造方法 ) 1.方法访问标志 2.方法名称索引 3.方法返回类型 4.方法属性数量 前言 上一篇博客 [Java 虚 ...

  3. 【Java 虚拟机原理】Class 字节码二进制文件分析 三 ( 访问和修饰标志 | 类索引 | 父类索引 | 接口计数器 | 接口表 | 字段计数器 | 字段表 )

    文章目录 前言 一.访问和修饰标志 二.类索引 三.父类索引 四.接口计数器 五.接口表 六.字段计数器 七.字段表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( ...

  4. 【Java 虚拟机原理】Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | info[] | 完整分析字节码文件中的常量池二进制数据 )

    文章目录 前言 一.常量池结构分析 1.常量池位置 2.常量池结构 3.常量池单个常量 4.常量池单个常量 tag 标签 二.常量池字节码文件分析 0.常量池附加信息 1.常量池 #1 常量分析 2. ...

  5. 【Java 虚拟机原理】Class 字节码二进制文件分析 七 ( 局部变量表分析 )

    文章目录 前言 一.编译生成带局部变量表的字节码文件 二.局部变量表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | i ...

  6. 【Java 虚拟机原理】Class 字节码二进制文件分析 四 ( 字段表数据结构 | 字段表详细分析 | 访问标志 | 字段名称 | 字段描述符 | 属性项目 )

    文章目录 前言 一.字段表总数据结构 二.访问标志 三.字段名称 四.字段描述符 五.属性项目数 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 三 ( 访问和修饰标志 | ...

  7. 【Java 虚拟机原理】动态字节码技术 | Dalvik ART 虚拟机 | Android 字节码打包过程

    文章目录 一.动态字节码技术 二.Dalvik & ART 虚拟机 三.Android 字节码打包过程 总结 一.动态字节码技术 动态字节码技术 就是在 运行时 , 动态修改 Class 字节 ...

  8. 大神程序员都懂英文翻译,而你却因英语不行遭拒?

    程序员中很多人都是在外企里面工作的.这样的话就会涉及到,跟外籍同事的沟通和协作.上下级之间的汇报工作.虽然我们的主要工作是开发软件.是技术类工作.跟计算机打交道比较多,但是沟通对技术来说,还是非常重要 ...

  9. 程序员都懂的30个段子,你知道几个?

    1. 问:程序猿最讨厌康熙的哪个儿子?. A.皇长子允禔 C.皇八子允禩 B.皇三子允祉 D.皇十六子允禄 答案C皇八子胤禩.因为他是八阿哥(bug) 2. 程序猿要了3个孩子,分别取名叫Ctrl.A ...

最新文章

  1. java optional 用法_Java8 中的 Optional 相关用法
  2. 那些实用与颜值齐飞的桌面!
  3. Queue)) 类,msdn上的一篇文章,便于查看
  4. 51nod 1172 Partial Sums V2 卡精度的任意模数FFT
  5. Spring源码版本命名规则及下载安装(下)笔记
  6. 工业路由器和工业交换机的区别介绍
  7. Java底层抛出异常_总结java的exception
  8. 使用Java编一个有意思的_一个有趣的Java编译问题
  9. Java算法——回溯法
  10. GMM 简介与 Stata 实现
  11. 电脑ping,电脑ping怎么用|电脑ping怎么打开_PC6教学
  12. 【工业视觉】Halcon常用教程
  13. 雷达探测项目仿真代码(Matlab代码实现)
  14. pgsql 筛选中文字符正则_PostGreSql 判断字符串中是否有中文的案例
  15. 现代OpenGL教程 02——贴图
  16. Web 智能代码编辑器 WeBuilder 2022
  17. iPhone 13系列电池信息曝光,续航稳了
  18. tts代表_有什么好的文字转语音(TTS)的软件推荐?
  19. 2023五一加班工资三倍怎么算(附详细计算公式)
  20. 【自动驾驶-3D目标检测】3DSSD:基于点的3D单级目标检测器

热门文章

  1. 在Ubuntu虚拟机使用ffmpeg采集摄像头的yuv视频数据
  2. Parasoft案例研究:医疗器械软件验证与合规性
  3. 坑爹!攻城狮老爸被苹果解雇 只因女儿晒iPhone X!
  4. Dreamweaver CS 5 快捷键大全
  5. 锁屏状态下,看到歌手图片
  6. Java常见面试题_理论+实践
  7. Bentley 软件公司发起 2021 基础设施数字化光辉大奖赛项目征集活动
  8. Windows环境下启动zookeeper错误:Zookeeper audit is disabled
  9. nmap系统版本扫描
  10. 俄罗斯方块游戏开发(VC++ mfc dialog模式)