18 | 即时编译器的中间表达形式
该思维导图由 AI 生成,仅供参考
1. 中间表达形式(IR)
- 深入了解
- 翻译
- 解释
- 总结
即时编译器中的中间表达形式(IR)对于编译器优化至关重要。Sea-of-Nodes类型的SSA IR去除了变量的概念,直接采用变量所指向的值进行运算,使得编译器可以进行各种优化,如常量折叠、常量传播、强度削减和死代码删除等。其中,Global Value Numbering(GVN)是一种发现并消除等价计算的优化技术,通过GVN可以将重复的计算合并,从而降低输出机器码的大小。在Sea-of-Nodes中,由于只存在值的概念,GVN算法变得非常简单。通过GVN的优化,可以在IR图上实现公共子表达式消除,进一步提高编译器的效率。整体而言,本文深入浅出地介绍了即时编译器中的中间表达形式及其优化过程,对于理解编译器优化具有重要参考价值。
《深入拆解 Java 虚拟机》,新⼈⾸单¥59
全部留言(20)
- 最新
- 精选
- ext4除了你上面提到的内存依赖,我看到C2的ideal graph里面还有一种依赖叫做I/O dependency,这个在Graal的graph里似乎也没有了。可以解释一下C2的这个I/O dependency是做什么的,以及Graal是如何替代这种依赖的表示的么?
作者回复: 首先这些dependency都是为了scheduling服务的,也就是把图序列化为指令序列,图节点中的相互依赖会让它们拥有先后关系。 个人理解哈,原本的IO dependency就是IO顺序,Cliff Click大神的论文中说的是没有内存映射的IO访问。现在C2应该用来泛指非内存访问的JVM状态。 Graal里这种额外的依赖关系都是用控制流先后顺序来表达的。
2018-08-317 - ShineIR图有点看不懂。基本块是根据什么原则划分的? 有些块有start,begin, end等等,有些块却没有? 为什么GVN代码中,都是判断a,b是否大于0,图中B3来了一个Merge节点?
作者回复: 先说一下,这个是Graal的IR。 基本块的划分规则是根据其定义来的,即最长的,顺序执行的节点集。 start begin end是为了标注基本块的起始和结尾,便于优化,没有其他实际意义。 merge节点可以看成控制流交汇的节点,在Graal里就是用来挂phi节点的。最后有两个return而没有merge,实际上是另外一个优化code duplicati,将原本的merge优化掉,生成两条独立程序路径。在这两条路径中,b的值是唯一的,而不是一个phi方法。
2018-09-034 - Ken张云忠郑老师,本节中第一张IR图和下面的Control Flow图是使用http://ssw.jku.at/General/Staff/TW/igv.html的IdealGraphVisualizer查看的吧. 这个工具使用jdk7启动起来后,但是在执行时必须要使用debug版的jdk7才能执行参数-XX:PrintIdealGraphLevel=2 -XX:PrintIdealGraphFile=ideal.xml,一直困扰在获取不到debug版的jdk7,下载openjdk7自己编译过程中遇到了太多问题,尤其是build-debug/hotspot中太多代码编译不过去的问题. 老师是怎么样一步步得到debug版的jdk7的?
作者回复: 那个比较老,你可以去下载GraalVM,里面有附带igv的。OpenJDK 7编译是比较难,很多奇奇怪怪的依赖问题,8之后就好多了
2020-01-173 - likun你好 我这边找不到bebug版本的jdk10,好像无法查看ir图
作者回复: 导出Graal IR不用debug版本的JDK
2018-10-22 - 夜行观星看懂这篇文章,已经是一年之后,时间真快2019-11-19617
- 搬砖的孟达看不太懂。哈哈哈...可能基础还不到这个水平吧,多看多思考吧。2018-12-0913
- the geek这篇文章最好还是看懂,后面的方法内联章节会经常出现IR图,我一开始也是看了个大概,看了方法内联后,回来静下心一看,还是比较简单的 主要就是将方法的执行流程转换为IR图。 IR图中一些符号解释(以下是个人简单理解,仅供参考): 1. 常量值:C(0)、C(1)。就是常量值1、2 (类型是i32) 2. 参数值P(0)、P(1)。就是方法参数0和方法参数1=>上面int a,int b 3.Phi(IR节点1,IR节点2,内存类型)。(i32可能是说int 32位 ,方便分配内存吧?个人猜测老师指正)2020-02-058
- 房艳看了好几遍,边看边整理思路,看懂个大概。这是我边看边画的知识点: java源代码---java编译器--->java字节码---即时编译器--->机器码 编译器:前端:IR(词法分析、语法分析、语义分析--->生成中间表达形式) 后端:IR优化--->生成目标代码 即时编译器直接将java字节码作为一种IR,即时编译器将java字节码转为SSA IR(IR图) 静态单赋值IR(SSA IR):每个变量只能被赋值一次,而且只有当变量被赋值之后才能使用 SSA IR存在的问题:不同执行路径会对同一个变量设置不同的值 解决方法:Phi函数-能够根据不同的执行路径选择不同的值 1.C2采用Sea-of-Nodes的SSA IR,去除变量的概念,直接采用变量所指向的值进行运算。 如:Phi(y1,y2) ---> Phi(1,0) C2没有固定节点的概念,所有的IR节点都是浮动节点。将根据各个基本块头尾之间的控制依赖,以及数据依赖和内存依赖,来进行节点调度。 节点调度:在编译过程中,编译器需要(多次)计算浮动节点具体的排布位置。这个过程称为节点调度。 内存依赖:假设一段程序往内存中存储了一个值,而后又读取同一内存,那么显然程序希望读取到的是所存储的值。 即时编译器不能任意调度对同一内存地址的读写,因为它们之间存在依赖关系。 C2的做法便是将这种时序上的先后记录为内存依赖,并让节点调度算法在进行调度时考虑这些内存依赖关系。 2.Graal的IR也是Sea-of-Nodes类型,可理解为是C2 IR的精简版本 Graal将内存读写转换成固定节点。由于固定节点存在先后关系,因此无需额外记录内存依赖。 IR的可视化工具 IGV:被控制流边所连接的是固定节点,其他的皆属于浮动节点。 IR图是竖着看,然后遇到if分支就会有两个流程(满足与不满足),hpi函数里面用了节点上的编号,我理解是这样。 GVN是一种发现并消除等价计算的优化技术。例如如果一段程序中出现多次相同的乘法,那么即时编译器可以将这些乘法合并为一个。 在 Sea-of-Nodes 中,由于只存在值的概念,因此 GVN 算法将非常简单: 如果一个浮动节点本身不存在内存副作用(由于 GVN 可能影响节点调度,如果有内存副作用的话,那么将引发一些源代码中不可能出现的情况) , 那么即时编译器只需判断该浮动节点是否与已存在的浮动节点的类型相同,所输入的 IR 节点是否一致,便可以将这两个浮动节点归并成一个。 有个问题:内存副作用是什么意思?有点不理解,麻烦帮我解答一下。2021-01-194
- neohope想问一下老师,idealgraphvisualizer中,有没有办法看到全局的IR图?开启后,好像有很多次优化,每次都只能看到一部分哦。 此外: 1、官网下载的idealgraphvisualizer是2011年版本,没法用,要用直接在github上下载的版本 2、idealgraphvisualizer当前版本,好像只支持JDK1.8? 3、从graal官网下载的版本只有JDK1.8,下载了也没有用。直接下载Oracle JDK11就可以了 4、最后例子的Demo,JDK11参数要调整一下: java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler -XX:CompileCommand=dontinline,"CompilationTest.hash()" -Dgraal.Dump=:3 -Dgraal.OptDeoptimizationGrouping=false CompilationTest 5、前面两个例子,需要用Debug版本的JDK。最后一个不需要。2019-09-0234
- 鱼肚原本里的 IGV 用不了,用这个 https://github.com/oracle/graal/releases/tag/idealgraphvisualizer-5432019-09-033