[讨论] 请教HotSpot C2寄存器分配中OptoReg的意义

LeafInWind 2014-08-13
看HotSpot C2寄存器分配,一直没有搞清楚OptoReg的意义,为什么不能直接使用VMReg,为什么OptoReg的0号对应R10,2号对应R11,...。ad文件中有一段描述,大致意思是要把优先分配的寄存器定义为小序号的OptoReg,又说NS的优先级高于SOC,SOC又高于SOE,...。这段完全没有搞懂,不知有谁能解答一下。谢谢!
RednaxelaFX 2014-08-14
LeafInWind 写道
呼唤R神

别急…最近有点事情,上网时间极其不固定

sparc.ad文件里有这么一段注释:
// ----------------------------
// Specify the enum values for the registers.  These enums are only used by the
// OptoReg "class". We can convert these enum values at will to VMReg when needed
// for visibility to the rest of the vm. The order of this enum influences the
// register allocator so having the freedom to set this order and not be stuck
// with the order that is natural for the rest of the vm is worth it.

我觉得这个已经足以解释为什么要在VMReg之外单独弄一个类型了。

HotSpot VM的runtime部分(包括具体的汇编器)能认识的表示寄存器的类型是VMReg。而OptoReg是C2专用的。两者之间可以相互转换。HotSpot VM有传统倾向于把JIT编译器和VM的其它部分尽可能分离开。所以编译器与VM之间的“接触面”很可能会有些看似重复的、或者是包装用的数据类型。几乎整个ci都是这个用途。OptoReg算是个例外,在ci之外但也是作为这种“隔离”用途的。这样编译器就有更大的自由去做各种不同的决定,最终要跟VM交互的时候再转换为VM能认识的类型。

LeafInWind 写道
又说NS的优先级高于SOC,SOC又高于SOE,...。这段完全没有搞懂

至于这段…楼主没搞懂的是那些缩写么?
NS:no save
SOC:save-on-call
SOE:save-on-entry

其实换种更通用的说法就是:
NS:scratch register
SOC:caller-save register
SOE:callee-save register

这样明白了?
LeafInWind 2014-08-14
ad文件中定义的某些寄存器,例如
// General Registers
// "reg_def"  name ( register save type, C convention save type,
//                   ideal register type, encoding );
reg_def RBX  (SOC, SOE, Op_RegI,  3, rbx->as_VMReg());

为什么register save type和C convention save type不一致,请问C2在分配RBX时到底是将它当做SOC还是SOE来使用的?

另外,Matcher类中部分变量,如_old_SP,_in_arg_limit,_new_SP,_out_arg_limit,都是OptoReg::Name类型,请问OptoReg的意义是否是为了能以某种方式一致的处理C2编译栈帧,就像VMReg使得物理寄存器和栈位置可以被统一处理一样。

最后,能否请R神解释一下下面这个函数的逻辑
int PhaseRegAlloc::reg2offset_unchecked( OptoReg::Name reg ) const {
  // Slots below _max_in_arg_stack_reg are offset by the entire frame.
  // Slots above _max_in_arg_stack_reg are frame_slots and are not offset.
  int slot = (reg < _matcher._new_SP)
    ? reg - OptoReg::stack0() + _framesize
    : reg - _matcher._new_SP;
  // Note:  We use the direct formula (reg - SharedInfo::stack0) instead of
  // OptoReg::reg2stack(reg), in order to avoid asserts in the latter
  // function.  This routine must remain unchecked, so that dump_frame()
  // can do its work undisturbed.
  // %%% not really clear why reg2stack would assert here

  return slot*VMRegImpl::stack_slot_size;
}
LeafInWind 2014-08-22
自己回答一下的,请大家指教!
在ad文件中,关于C2栈帧,有如下一段注释
代码清单1
//----------FRAME-----------------------------------------------
// Definition of frame structure and management information.
//
//  S T A C K   L A Y O U T    Allocators stack-slot number
//                             |   (to get allocators register number
//  G  Owned by    |        |  v    add OptoReg::stack0())
//  r   CALLER     |        |
//  o     |        +--------+      pad to even-align allocators stack-slot
//  w     V        |  pad0  |        numbers; owned by CALLER
//  t   -----------+--------+----> Matcher::_in_arg_limit, unaligned
//  h     ^        |   in   |  5
//        |        |  args  |  4   Holes in incoming args owned by SELF
//  |     |        |        |  3
//  |     |        +--------+
//  V     |        | old out|      Empty on Intel, window on Sparc
//        |    old |preserve|      Must be even aligned.
//        |     SP-+--------+----> Matcher::_old_SP, even aligned
//        |        |   in   |  3   area for Intel ret address
//     Owned by    |preserve|      Empty on Sparc.
//       SELF      +--------+
//        |        |  pad2  |  2   pad to align old SP
//        |        +--------+  1
//        |        | locks  |  0
//        |        +--------+----> OptoReg::stack0(), even aligned
//        |        |  pad1  | 11   pad to align new SP
//        |        +--------+
//        |        |        | 10
//        |        | spills |  9   spills
//        V        |        |  8   (pad0 slot for callee)
//      -----------+--------+----> Matcher::_out_arg_limit, unaligned
//        ^        |  out   |  7
//        |        |  args  |  6   Holes in outgoing args owned by CALLEE
//     Owned by    +--------+
//      CALLEE     | new out|  6   Empty on Intel, window on Sparc
//        |    new |preserve|      Must be even-aligned.
//        |     SP-+--------+----> Matcher::_new_SP, even aligned
//        |        |        |
//

但这个栈帧并不是C2编译器在进行寄存器分配时看到的栈帧,在reg allocator眼中的基于OptoReg值的栈帧我认为如下代码清单2所示:
代码清单2
//     |        |
// --- +--------+---->
//  ^  |  pad1  | 
//  |  +--------+
//  |  |        | 
//  f  | spills |  
//  r  |        |  
//  a  +--------+----> Matcher::_out_arg_limit, unaligned
//  m  |        |  
//  e  |  out   |  
//     |        |  
//  s  |  args  |  
//  z  |        |  
//  |  +--------+
//  |  | new out|  
//  V  |preserve|  
//  ---+--------+----> Matcher::_new_SP, even aligned
//     |  pad0  |        
//     +--------+----> Matcher::_in_arg_limit, unaligned
//     |   in   |  
//     |  args  |  
//     |        |  
//     +--------+
//     | old out|  
//     |preserve|    
//     +--------+----> Matcher::_old_SP, even aligned
//     |   in   |  
//     |preserve|  
//     +--------+
//     |  pad2  |  
//     +--------+  
//     | locks  |  
//     +--------+----> OptoReg::stack0(), even aligned

其中_framesize在寄存器分配之初并没有实际值,只有到所有寄存器都已确定,才通过如下代码计算得到。
  if( _max_reg <= _matcher._new_SP )
    _framesize = C->out_preserve_stack_slots();
  else _framesize = _max_reg -_matcher._new_SP;
  _framesize = round_to(_framesize, Matcher::stack_alignment_in_slots());

其中_max_reg -_matcher._new_SP就是_new_SP之上的那段空间,实际代表的是当前方法需要的spill slots数目加上最大的out args slots数目。因此reg2offset_unchecked函数的逻辑如上一层楼,将一个OptoReg值转换为当前栈帧中相对SP的偏移。
至于reg allocator为什么要把栈帧看做代码清单2的样子,还没有想清楚!!??
LeafInWind 2014-08-22
关于NS,SOC和SOE的分配次序,我认为是因为NS不用保存,因此分配它没有任何代价,应该优先分配;而SOC通常用于分配给短命的值(见engineering a compiler P310),因此分配它的代价较小,故次优先分配;而SOE通常用于分配给长命的值,因此不到最后不使用它。
Global site tag (gtag.js) - Google Analytics