【深入理解Java 虚拟机之 即时编译器】1151 深入理解Graal编译器

一、历史背景

1、Graal虚拟机以及Graal编译器仍在实验室中尚未商用,但未来其有望代替或成为HotSpot下一代技术基础。Graal编译器最初是在Maxine虚拟机[插图]中作为C1X编译器[插图]的下一代编译器而设计的,所以它理所当然地使用于Java语言来编写。

2、2012年,Graal编译器从Maxine虚拟机项目中分离,成为一个独立发展的Java编译器项目。

3、Graal编译器在JDK 9时以Jaotc提前编译工具的形式首次加入到官方的JDK中,从JDK 10起,Graal编译器可以替换服务端编译器,成为HotSpot分层编译中最顶层的即时编译器。这种可替换的即时编译器架构的实现,得益于HotSpot编译器接口的出现。

4、早期的Graal曾经同C1及C2一样,与HotSpot的协作是紧耦合的,这意味着每次编译Graal均需重新编译整个HotSpot。JDK 9时发布的JEP 243:Java虚拟机编译器接口(Java-Level JVM Compiler Interface,JVMCI)使得Graal可以从HotSpot的代码中分离出来。

5、JVMCI 三种功能

A: 响应HotSpot的编译请求,并将该请求分发给Java实现的即时编译器

B: 允许编译器访问HotSpot中与即时编译相关的数据结构,包括类、字段、方法及其性能监控数据等,并提供了一组这些数据结构在Java语言层面的抽象表示。

C: 提供HotSpot代码缓存(Code Cache)的Java端抽象表示,允许编译器部署编译完成的二进制机器码。

利用三种功能可以把一个在HotSpot虚拟机外部的、用Java语言实现的即时编译器(不局限于Graal)集成到HotSpot中,响应HotSpot发出的最顶层的编译请求,并将编译后的二进制代码部署到HotSpot的代码缓存中。

6、Graal和JVMCI的出现,为不直接从事Java虚拟机和编译器开发,但对Java虚拟机技术充满好奇心的读者们提供一条窥探和尝试编译器技术的良好途径,现在我们就将开始基于Graal来实战HotSpot虚拟机的即时编译与代码优化过程。

二、构建编译调试环境

1、为了降低代码管理、依赖项管理、编译和测试等环节的复杂度,Graal团队专门用Python 2写了一个名为mx的小工具来自动化做好这些事情。

2、先安装mx

# git clone https://github.com/graalvm/mx.git
# 倒入环境变量
export PATH = 'pwd'/mx:$PATH

说明

找一个合适的JDK来编译。考虑到Graal VM项目是基于OpenJDK 8开发的,而JVMCI接口又在JDK 9以后才会提供,所以Graal团队提供了一个带有JVMCI功能的OpenJDK 8版本,我们可以选择这个版本的JDK 8来进行编译。当读者只关注Graal编译器在HotSpot上的应用而不想涉及Graal VM其他方面时,可直接采用JDK 9及之后的标准Open/OracleJDK。

需要把IDE配置中使用的Java堆修改到2GB或以上,才能保证Graal在IDE中的编译构建能够顺利进行

3、配置好java 对应版本的环境变量

export JAVA_HOME=/usr/lib/jvm/oraclejdk1.8.0...

4 、是获取Graal编译器代码

# git clone https://github.com/graalvm/graal.git
# cd /graal/comiler
# mx build

三、JVMCI编译器接口

1、JVMCI接口的核心内容实际

字节码、部变量表中变量槽的个数、操作数栈的最大深度,还有分层编译在底层收集到的统计信息等。

2、接口

interface JVMCICompiler {
    
}

interface CompliationRequest {
    
}

interface JavaMethod {
    
}

四、代码中间表示

1、代码在编译器内部过程

字节码→理想图→优化→机器码(以Mach Node Graph表示)的转变过程。

2、理想图是一种有向图,用节点来表示程序中的元素,譬如变量、操作符、方法、字段等,而用边来表示数据或者控制流。

A: 数据流笔者使用蓝色线(以虚线表示),控制流使用红色线(以实线表示))

B: 理想图本质上就是这种将数据流图和控制流图以某种方式合并到一起,用一种边来表示数据流向,另一种边来表示控制流向的图形表示。

在这里插入图片描述

参数0(记作P(0))和参数1(记作P(1))是如何送入加法操作的,然后结果是如何和常量2(记作C(2))一起送入除法操作的。

3、公共字表达式的消除
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

五、代码优化与生成

1、每一个理想图的节点都有两个共同的主要操作,一个是规范化(Canonicalisation),另一个是生成机器码(Generation)。

2、规范化则是指如何缩减理想图的规模,也即在理想图的基础上优化代码所要采取的措施。

3、想图中发现了可以进行消除的算术子表达式,那就找出重复的节点,然后替换、删除。

例子:

两个整数相加那么简单的操作,也会尝试过了常量折叠(如果两个操作数都为常量,则直接返回一个常量节点)、算术聚合(聚合树的常量子节点,譬如将(a+1)+2聚合为a+3)、符号合并(聚合树的相反符号子节点,譬如将(a-b)+b或者b+(a-b)直接合并为a)等多种优化。

4、Graal并不是直接由理想图转换到机器码,而是和其他编译器一样,会先生成低级中间表示(LIR,与具体机器指令集相关的中间表示),然后再由HotSpot统一后端来产生机器码。

如涉及算术运算加法的操作,就在ArithmeticLIRGeneratorTool接口的emitAdd()方法里完成

5、Graal编译器能够支持的目标平台

目前它只提供了三种目标平台的指令集(SPARC、x86-AMD64、ARMv8-AArch64)的低级中间表示。

6、规范化并不局限于单个操作码的局部范围之内,很多的优化都是要立足于全局来进行的,这类操作在CanonicalizerPhase类中完成。

猜你喜欢

转载自blog.csdn.net/qq_40996741/article/details/109173594
今日推荐