[讨论] [HotSpot VM] JIT编译以及执行native code的流程

yuyinyang 2014-04-13
额,怎么说呢。。
Thomas那篇硕士论文的3.5节提到:
Thomas Würthinger 写道
The server compiler selects subtrees out of the ideal graph and converts them one by one. It selects specific nodes as root nodes and transforms their related tree using tree selection rules.

这个阶段是否就是ideal graph node转换成MachNode的阶段,对应的源码在什么地方?还有论文以及代码里都提到的一个“matcher”具体是如何工作的?
另外您说的代码生成阶段套模板是指根据ad文件中描述的规则生成指令吗?
RednaxelaFX 2014-04-14
yuyinyang 写道
Thomas那篇硕士论文的3.5节提到:
Thomas Würthinger 写道
The server compiler selects subtrees out of the ideal graph and converts them one by one. It selects specific nodes as root nodes and transforms their related tree using tree selection rules.

这个阶段是否就是ideal graph node转换成MachNode的阶段,

对的。这段是instruction selection,在C2里通过一个bottom-up rewrite system(BURS)来实现。
在之前我提到的C2讨论帖里也有讲到BURS,一些相关资料啥的。请参考那边。

yuyinyang 写道
对应的源码在什么地方?

对应的源码,平台无关的部分在hotspot/src/share/vm/adlc的整个目录,和hotspot/src/share/vm/opto/matcher.[hpp|cpp]。平台相关的部分在hotspot/src/cpu/<arch>/vm/<arch>.ad文件里。
ad文件里很重要的部分就是指定树的匹配模式及其对应的MachNode节点。
在构建HotSpot VM时,会先编译adlc(Architecture Description Language Compiler),然后执行adlc把ad文件编译成一个DFA,作为平台相关部分的Matcher的实现,以及MachNode的类型声明啥的。

Matcher是从C2的Compile::Code_Gen()创建Matcher实例并调用matcher.match()来进入的。

yuyinyang 写道
还有论文以及代码里都提到的一个“matcher”具体是如何工作的?

Matcher就是C2的BURS的核心。参照上面的描述,先读读相关资料了解一下它想做的是啥再回来看C2的实现。
yuyinyang 写道
另外您说的代码生成阶段套模板是指根据ad文件中描述的规则生成指令吗?

我说的是狭义的code generation,是最后从MachNode生成出机器码的地方,对应C2的代码是Compile::Output()。C2的MachNode跟机器码几乎一一对应,所以这里要做的就是把对应的机器码输出出来而已。

“Code generator”在编译器里有很多种用法,有广义有狭义。最广义的情况下,整个编译器后端都可以看作是“code generator”。最狭义的情况下,只有把最低层的IR映射到机器码的部分叫做“code generator”。所以一定得指定清楚说的是什么范围才回答得了。

就楼主问的问题看,想问的应该是指C2的Compile::Code_Gen()所覆盖的范围?那个已经覆盖了C2的整个后端(所有平台相关的优化啥的都在里面)。
yuyinyang 2014-04-15
R神,您推荐那篇A Tutorial on Adding New Instructions to the Oracle® Java HotSpot TM Virtual Machine里有这样一段介绍编译过程的:



不是先parse成ideal graph的吗,这里怎么又说是abstract syntax tree了。。而且根据后面内容讲的貌似就是根据这些tree去匹配ad文件里定义的规则然后选择指令,所以这个tree就是output()之前的最后形态了吗?。。那么它是下面这张图里哪个阶段的输出呢?总是感觉不能把Ideal Graph Node,MachNode,Subtrees,CFG与compile的各个phase确切地对应起来。。
RednaxelaFX 2014-04-16
呵呵,AMD那篇不能太追究其术语的准确性…它只有后面讲如何添加新指令的部分比较实用。

Java字节码parse之后就是Ideal Graph。这种图不是树,而是普通的有向图:它代表控制流的节点构成的图是可以有环的有向图;而其代表数据依赖的节点如果不看Phi的话是有向无环图(DAG),算上Phi节点就有可能有环了。

Thomas的Ideal Graph Visualizer论文里有提到Ideal Graph在做instruction selection之前会先变成树,因为C2的Matcher只能匹配树而不能匹配DAG。这就意味着某些节点可能会被复制出多份。多半是因为这个原因,AMD那篇文档只关心instruction selection部分,只看到了树型的输入,所以就认为输入是抽象语法树了。
这个步骤可以算在图中Geneate MachNode Graph(instruction selection)里面。但要注意它是发生在实际的instruction selection之前,算是必要的准备步骤。
具体在代码里,instruction selection的整个动作就在Matcher::match()里。其中这段代码就是把Ideal Graph变成概念上的树的形式的:
  // ---------------
  // Collect roots of matcher trees.  Every node for which
  // _shared[_idx] is cleared is guaranteed to not be shared, and thus
  // can be a valid interior of some tree.
  find_shared( C->root() );
  find_shared( C->top() );


既然要复制节点,那就得选择哪些节点可以复制而哪些不能。所有可能有副作用的节点都不可以复制,它们一定要被匹配规则匹配为一棵子树的根;而没有副作用的节点则可以被复制,它们可以被匹配到任意位置。
Matcher::find_shared(Node *n)里,被set_shared(n)的节点就是标记为不能复制的,没被标记的就是可以复制的。然后还有set_dontcare(n)的节点,那些多半是无所谓或者无法匹配的节点。
yuyinyang 2014-06-09
R神,HotSpot运行阶段(包括JIT编译生成代码)有没有那种显式地清空所有寄存器的行为?有没有某些寄存器是一直不会被用到的?比如index比较大的那些xmm寄存器?
有没有可能我在VM初始化阶段为一个不会被用到的寄存器(假设是xmm7)附上某个值,之后可以一直使用它?
RednaxelaFX 2014-06-10
yuyinyang 写道
R神,HotSpot运行阶段(包括JIT编译生成代码)有没有那种显式地清空所有寄存器的行为?有没有某些寄存器是一直不会被用到的?比如index比较大的那些xmm寄存器?
有没有可能我在VM初始化阶段为一个不会被用到的寄存器(假设是xmm7)附上某个值,之后可以一直使用它?

您不能依赖某个GPR没被HotSpot使用…
例如,看看ad文件,里面声明了的寄存器都会被C2的寄存器分配器使用(除了RIP/RSP/RBP)。所以那些你都不能指望值放进去了不会变。x86的ISA上暴露出来的通用寄存器那么少,当然得用到尽咯。

我印象中HotSpot在x86上没用gs段寄存器。您要放的值假如是个地址的话可以放在gs里,然后要访问它的时候指定段寄存器就是。
yuyinyang 2014-06-10
我不指望exclusive地使用某个GPR...我做了一件非常naive的事情,我把hotspot里所有关于xmm8-15的声明和使用的地方全部remove了(包括ad文件等),然后跑了一些benchmark也都顺利通过了,这是否能保证hotspot不会用到这些寄存器了?

我在看JIT生成代码的那段output方法,有个地方不太明白。JIT貌似会先为每个CFG block根据它所用到的instructions的大小计算block初始的大小blk_starts[i],然后实际generate出来的大小是cb->insts_size()。我想知道它到底是如何确定block和instruction的初始size的呢?
yuyinyang 2014-06-16
上面的问题已经找到了,一些指令的大小是固定的写在adfile里的,没有在adfile里指定大小的会通过把指令emit到一个trash code buffer里的计算出指令实际的size
又碰到新的问题,挂在instruction selection的阶段,下面这个dump出来的东西完全看不明白。。
引用

o35     MulD    === _ o33 o34  [[o36 ]]

--N: o35        MulD    === _ o33 o34  [[o36 ]]

   --N: o33     ConvI2D === _ o10  [[o35 ]]
   REGD  100  convI2D_reg_reg
   STACKSLOTD  195  storeSSD

      --N: o10  Parm    === o3  [[o33 ]] Parm0: int
      RREGI  0  RREGI
      RAX_REGI  0  RAX_REGI
      RBX_REGI  0  RBX_REGI
      RCX_REGI  0  RCX_REGI
      RDX_REGI  0  RDX_REGI
      RDI_REGI  0  RDI_REGI
      NO_RCX_REGI  0  NO_RCX_REGI
      NO_RAX_RDX_REGI  0  NO_RAX_RDX_REGI
      STACKSLOTI  100  storeSSI

   --N: o34     ConvF2D === _ o11  [[o35 ]]
   REGD  100  convF2D_reg_reg
   STACKSLOTD  195  storeSSD
   _ConvF2D_regF_  0  _ConvF2D_regF_

      --N: o11  Parm    === o3  [[o34 ]] Parm1: float
      REGF  0  REGF
      STACKSLOTF  95  storeSSF

# To suppress the following error report, specify this argument
# after -XX: or in .hotspotrc:  SuppressErrorAt=/matcher.cpp:1513
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (/home/yuyinyang/openjdk-b24-28_aug_2012/hotspot/src/share/vm/opto/matcher.cpp:1513), pid=234697, tid=140299994089216
#  assert(false) failed: bad AD file
#
# JRE version: 7.0
# Java VM: OpenJDK 64-Bit Server VM (23.2-b09-fastdebug mixed mode linux-amd64 compressed oops)
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /home/yuyinyang/workspace/test/hs_err_pid234697.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.sun.com/bugreport/crash.jsp
#
Current thread is 140299994089216
Dumping core ...
Aborted

hs_err文件里的部分内容:
引用

---------------  T H R E A D  ---------------

Current thread (0x00007f9ba4309000):  JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=234856, stack(0x00007f9a233f4000,0x00007f9a234f5000)]

Stack: [0x00007f9a233f4000,0x00007f9a234f5000],  sp=0x00007f9a234eff40,  free space=1007k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xa7cfba]  VMError::report(outputStream*)+0xf6e
V  [libjvm.so+0xa7e285]  VMError::report_and_die()+0x5dd
V  [libjvm.so+0x5362db]  report_vm_error(char const*, int, char const*, char const*)+0x8c
V  [libjvm.so+0x85eb7b]  Matcher::Label_Root(Node const*, State*, Node*, Node const*)+0x4ef
V  [libjvm.so+0x85ea7b]  Matcher::Label_Root(Node const*, State*, Node*, Node const*)+0x3ef
V  [libjvm.so+0x85e26a]  Matcher::match_tree(Node const*)+0x1d0
V  [libjvm.so+0x85cc67]  Matcher::xform(Node*, int)+0x1a1
V  [libjvm.so+0x85a4a0]  Matcher::match()+0xb2c
V  [libjvm.so+0x4cb40c]  Compile::Code_Gen()+0xac
V  [libjvm.so+0x4c6cd8]  Compile::Compile(ciEnv*, C2Compiler*, ciMethod*, int, bool, bool)+0x1070
V  [libjvm.so+0x416a22]  C2Compiler::compile_method(ciEnv*, ciMethod*, int)+0xd2
V  [libjvm.so+0x4d7235]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0x391
V  [libjvm.so+0x4d6ace]  CompileBroker::compiler_thread_loop()+0x32c
V  [libjvm.so+0xa32250]  compiler_thread_entry(JavaThread*, Thread*)+0x57
V  [libjvm.so+0xa2de00]  JavaThread::thread_main_inner()+0x11e
V  [libjvm.so+0xa2dce0]  JavaThread::run()+0x110
V  [libjvm.so+0x8f29b1]  java_start(Thread*)+0x1bf


Current CompileTask:
C2:  25506   26             java.lang.StringCoding::access$000 (6 bytes)
Global site tag (gtag.js) - Google Analytics