13. Compiler data structures
Ideal Graph Visualizer
http://ssw.jku.at/General/Staff/TW/igv.html
etc/idealgraphvisualizer.conf:
default_options="-J-Xmx400m --branding
idealgraphvisualizer"
You need a fastdebug or a debug build
In OpenJDK build dir
$ make fastdebug_build
OR
$ make debug_build
13
14. Compiler data structures
Options to generate data for Ideal
Graph Visualizer
-XX:PrintIdealGraphLevel=0 [0:None, 4: most verbose]
-XX:PrintIdealGraphPort=4444
-XX:PrintIdealGraphAddress=”127.0.0.1”
-XX:PrintIdealGraphFile=<path to IR xml file>
IdealGraphVisualizer listens to port 4444 by default.
14
17. Compiler data structures
// Insert a new required input at the end
void Node::ins_req( uint idx, Node *n ) {
assert( is_not_dead(n), "can not use dead node");
add_req(NULL); // Make space
...
_in[idx] = n; // Stuff over old required edge
if (n != NULL) n->add_out((Node *)this); // Add
reciprocal def-use edge
}
void add_out( Node *n ) {
if (is_top()) return;
if( _outcnt == _outmax ) out_grow(_outcnt);
_out[_outcnt++] = n;
}
17
19. Compiler data structures
Node // Optimize functions
// more ideal node, canonicalize
virtual Node *Ideal(PhaseGVN *phase,
bool can_reshape);
// set of values this node can take
virtual const Type *Value
( PhaseTransform *phase ) const;
// existing node which computes same
virtual Node *Identity
( PhaseTransform *phase );
19
21. Compiler data structures
AddNode Ideal
Convert "(x+1)+2" into "x+(1+2)"
Convert "(x+1)+y" into "(x+y)+1"
Convert "x+(y+1)" into "(x+y)+1"
x Con1 Con2 x Con1 Con2
Add Add
Add Add
21
22. Compiler data structures
AddINode Ideal
Node* in1 = in(1);
Node* in2 = in(2);
int op1 = in1->Opcode();
int op2 = in2->Opcode();
// Fold (con1-x)+con2 into (con1+con2)-x
if ( op1 == Op_AddI && op2 == Op_SubI ) {
// Swap edges to try optimizations below
in1 = in2;
in2 = in(1);
op1 = op2;
op2 = in2->Opcode();
}
if( op1 == Op_SubI ) {
"(a-b)+(c-d)" into "(a+c)-(b+d)"
"(a-b)+(b+c)" into "(a+c)"
"(a-b)+(c+b)" into "(a+c)"
22
23. Compiler data structures
const Type *AddNode::Value(...)
// Either input is TOP ==> the result is TOP
// Either input is BOTTOM ==> the result is the local
BOTTOM
// Check for an addition involving the additive
identity
23
24. Compiler data structures
Node *AddNode::Identity(...)
// If either input is a constant 0, return the other
input.
const Type *zero = add_id(); // The additive identity
if( phase->type( in(1) )->higher_equal( zero ) )
return in(2);
if( phase->type( in(2) )->higher_equal( zero ) )
return in(1);
return this;
24
26. Compiler data structures
Node *PhaseGVN::transform_no_reclaim
// Return a node which computes the same function
// as this node, but in a faster or cheaper fashion.
while( 1 ) {
Node *i = k->Ideal(this, /*can_reshape=*/false);
if( !i ) break;...
}
const Type *t = k->Value(this); // Get runtime Value
set
k->raise_bottom_type(t);
Node *i = k->Identity(this); if (i != k) return i;
i = hash_find_insert(k); if( i && (i != k)) return i;
Parse で Node を作ると transform する。Parse しながら GVN
26
31. Compiler data structures
Node
^
ProjNode // project a single elem out of a tuple or
signature type
^
ParmNode // incoming Parameters
const uint _con; // The field in the
tuple we are projecting
const bool _is_io_use; // Used to distinguish
between the projections
// used on the control
and io paths from a macro node
31
32. Compiler data structures
Node
^
MergeMem // (See comment in memnode.cpp near
MergeMemNode::MergeMemNode for semantics.)
in(AliasIdxTop) = in(1) is always the top node
in(0) is NULL
in(AliasIdxBot) is a "wide" memory state.
For in(AliasIdxRaw) = in(3) and above, mem state for
alias type <N> or top
base_memory() // wide state
memory_at(N) // for alias type <N>
Identity: base が empty なら base を返す,さも無ければ this
Ideal: Simplify stacked MergeMem
32
34. Compiler data structures
class ConINode : public ConNode {
public:
ConINode( const TypeInt *t ) : ConNode(t) {}
virtual int Opcode() const;
// Factory method:
static ConINode* make( Compile* C, int con ) {
return new (C, 1) ConINode( TypeInt::make(con) );
}
class ConNode : public TypeNode {
public:
ConNode( const Type *t ) : TypeNode(t,1) {
init_req(0, (Node*)Compile::current()->root());
init_flags(Flag_is_Con);
}
class TypeNode : public Node {
const Type* const _type;
TypeNode( const Type *t, uint required ) : Node
34
35. Compiler data structures
// Add pointer plus integer to get pointer. NOT commutative, really.
// So not really an AddNode. Lives here, because people associate it with
// an add.
class AddPNode : public Node {
public:
enum { Control, // When is it safe to do this add?
Base, // Base oop, for GC purposes
Address, // Actually address, derived from base
Offset } ; // Offset added to address
AddPNode( Node *base, Node *ptr, Node *off ) : Node(0,base,ptr,off) {
init_class_id(Class_AddP);
}
Identity: if one input is 0, return in(Address), otherwise this
Ideal: 左が定数の加算であれば, expression tree を平坦化
raw pointer で NULL なら CastX2PNode(offset)
右が constant の加算なら (ptr + (offset+cn)) を (ptr + offset) +
con に変更
35
36. Compiler data structures
// Return from subroutine node
class ReturnNode : public Node {
public:
ReturnNode( uint edges, Node *cntrl, Node *i_o, Node *memory, Node
*retadr, Node *frameptr );
virtual int Opcode() const;
virtual bool is_CFG() const { return true; }
36
37. Compiler data structures
JVMState
JVMState* _caller // for scope chains
uint _depth, _locoff, _stkoff, _monoff,
uint _scloff // offset of scalar objs
uint _endoff
uint _sp
int _bci
ReexecuteState _reexecute
ciMethod* _method
SafePointNode* _map
37
38. Compiler data structures
class Type {
public:
enum TYPES { Bad = 0, Control,
Top,
Int, Long, Half, NarrowOop,
Tuple, Array,
AnyPtr, RawPtr, OopPtr, InstPtr, AryPtr, KlassPtr,
Function, Abio, Return_Address, Memory,
FloatTop, FloatCon, FloatBot,
DoubleTop, DoubleCon, DoubleBot,
Bottom, lasttype };
private:
const Type __dual;
protected:
const TYPES _base;
38
39. Compiler data structures
class Type {
:
public:
TYPES base();
static const Type *make(enum TYPES);
static int cmp(Type*, Type*);
int higher_equal( Type *t)
const Type *meet(Type *t);
virtual const Type *widen(Type *old, Type* limit)
virtual const Type *narrow(Type *old)
39
40. Compiler data structures
class Dict;
class Type;
class TypeD;
class TypeF;
class TypeInt;
class TypeLong;
class TypeNarrowOop;
class TypeAry;
class TypeTuple;
class TypePtr;
class TypeRawPtr;
class TypeOopPtr;
class TypeInstPtr;
class TypeAryPtr;
class TypeKlassPtr;
40
52. Parse do_one_bytecode
switch (bc()) {
case Bytecodes::_nop:
// do nothing
break;
case Bytecodes::_lconst_0:
push_pair(longcon(0)); break;
:
case Bytecodes::_iconst_5: push(intcon( 5)); break;
case Bytecodes::_bipush: push(intcon(iter
().get_constant_u1())); break;
case Bytecodes::_sipush: push(intcon(iter
().get_constant_u2())); break;
makecon, ingcon など定数を表すノードを返す static 関数もある。
52
53. Parse do_one_bytecode
case Bytecodes::_ldc:
case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w:
// If the constant is unresolved, run this BC once
in the interpreter.
{
ciConstant constant = iter().get_constant();
if (constant.basic_type() == T_OBJECT &&
!constant.as_object()->is_loaded()) {
int index = iter().get_constant_pool_index();
53
54. Parse do_one_bytecode
case Bytecodes::_aload_0:
push( local(0) );
break;
:
case Bytecodes::_aload:
push( local(iter().get_index()) );
break;
push, local は結果的に JVMState, SafePointNode の状態を変
更。
iter() を使って bytecode の引き数を取って来る事ができる。
54
55. Parse do_one_bytecode
case Bytecodes::_fstore_0:
case Bytecodes::_istore_0:
case Bytecodes::_astore_0:
set_local( 0, pop() );
break;
:
case Bytecodes::_fstore:
case Bytecodes::_istore:
case Bytecodes::_astore:
set_local( iter().get_index(), pop() );
break;
55
56. Parse do_one_bytecode
case Bytecodes::_pop: _sp -= 1; break;
case Bytecodes::_pop2: _sp -= 2; break;
case Bytecodes::_swap:
a = pop();
b = pop();
push(a);
push(b);
break;
case Bytecodes::_dup:
a = pop();
push(a);
push(a);
break;
56
57. Parse do_one_bytecode
case Bytecodes::_baload: array_load(T_BYTE); break;
case Bytecodes::_caload: array_load(T_CHAR); break;
case Bytecodes::_iaload: array_load(T_INT); break;
case Bytecodes::_saload: array_load(T_SHORT); break;
case Bytecodes::_faload: array_load(T_FLOAT); break;
case Bytecodes::_aaload: array_load(T_OBJECT); break;
case Bytecodes::_laload: {
a = array_addressing(T_LONG, 0);
if (stopped()) return; // guaranteed null or
range check
_sp -= 2; // Pop array and index
push_pair( make_load(control(), a, TypeLong::LONG,
T_LONG, TypeAryPtr::LONGS));
break;
}
57
58. Parse do_one_bytecode
case Bytecodes::_bastore: array_store(T_BYTE); break;
case Bytecodes::_castore: array_store(T_CHAR); break;
case Bytecodes::_iastore: array_store(T_INT); break;
case Bytecodes::_sastore: array_store(T_SHORT); break;
case Bytecodes::_fastore: array_store(T_FLOAT); break;
case Bytecodes::_aastore: {
d = array_addressing(T_OBJECT, 1);
if (stopped()) return; // guaranteed null or
range check
array_store_check();
c = pop(); // Oop to store
b = pop(); // index (already used)
a = pop(); // the array itself
const TypeOopPtr* elemtype = _gvn.type(a)-
>is_aryptr()->elem()->make_oopptr();
const TypeAryPtr* adr_type = TypeAryPtr::OOPS;
Node* store = store_oop_to_array(control(), a, d,
adr_type, c, elemtype, T_OBJECT);
58
59. Parse do_one_bytecode
case Bytecodes::_getfield:
do_getfield();
break;
case Bytecodes::_getstatic:
do_getstatic();
break;
case Bytecodes::_putfield:
do_putfield();
break;
case Bytecodes::_putstatic:
do_putstatic();
break;
59
65. Parse do_one_bytecode
case Bytecodes::_iinc: // Increment local
i = iter().get_index(); // Get local index
set_local( i, _gvn.transform(
new (C, 3) AddINode(
_gvn.intcon(iter().get_iinc_con()),
local(i) ) ) );
break;
65
66. Parse do_one_bytecode
_goto, _goto_w
int target_bci = (bc() == Bytecodes::_goto) ?
iter().get_dest() : iter().get_far_dest();
// If this is a backwards branch in the bytecodes,
add Safepoint
maybe_add_safepoint(target_bci);
// Update method data
profile_taken_branch(target_bci);
// Add loop predicate if it goes to a loop
if (should_add_predicate(target_bci)){
add_predicate();
}
// Merge the current control into the target basic
block
merge(target_bci);
...
66
67. Parse do_one_bytecode
_goto, _goto_w
...// See if we can get some profile data and hand
it off to the next block
Block *target_block = block()->successor_for_bci
(target_bci);
if (target_block->pred_count() != 1) break;
ciMethodData* methodData = method()->method_data();
if (!methodData->is_mature()) break;
ciProfileData* data = methodData->bci_to_data(bci
());
assert( data->is_JumpData(), "" );
int taken = ((ciJumpData*)data)->taken();
taken = method()->scale_count(taken);
target_block->set_count(taken);
break;
67
68. Parse do_one_bytecode
case _ifnull: btest = BoolTest::eq;
goto handle_if_null;
case _ifnonnull: btest = BoolTest::ne;
goto handle_if_null;
handle_if_null:
// If this is a backwards branch in the bytecodes,
add Safepoint
maybe_add_safepoint(iter().get_dest());
a = null();
b = pop();
c = _gvn.transform( new (C, 3) CmpPNode(b, a) );
do_ifnull(btest, c);
break;
68
69. Parse do_one_bytecode
case _if_acmpeq: btest = BoolTest::eq;
goto handle_if_acmp;
case _if_acmpne: btest = BoolTest::ne;
goto handle_if_acmp;
handle_if_acmp:
// If this is a backwards branch in the bytecodes,
add Safepoint
maybe_add_safepoint(iter().get_dest());
a = pop();
b = pop();
c = _gvn.transform( new (C, 3) CmpPNode(b, a) );
do_if(btest, c);
break;
69
71. Parse do_one_bytecode
case Bytecodes::_invokestatic:
case Bytecodes::_invokedynamic:
case Bytecodes::_invokespecial:
case Bytecodes::_invokevirtual:
case Bytecodes::_invokeinterface:
do_call();
break;
case Bytecodes::_checkcast:
do_checkcast();
break;
case Bytecodes::_instanceof:
do_instanceof();
break;
71
72. Parse do_one_bytecode
getClass はインライン展開され、
LoadKlass -> メモリアクセスに。
hashCode は static に
public class Call {
public static void main(String[] args) {
Call c = new Call();
for (int i = 0; i < 100000; i++) {
c.doit();
}
}
int doit() {
return getClass().hashCode();
}
}
72
73. Parse do_one_bytecode
case Bytecodes::_anewarray:
do_anewarray();
break;
case Bytecodes::_newarray:
do_newarray((BasicType)iter().get_index());
break;
case Bytecodes::_multianewarray:
do_multianewarray();
break;
case Bytecodes::_new:
do_new();
break;
73
82. Optimize
ConnectionGraph::compute_escape()
java object の allocation がなければ false を返す
AddP, MergeMem 等を work list にのせる、それらの out ものせる
worklist のノードを細かく調べる
GrowableArray<PointsToNode> _nodes に登録して、
GlobalEscape, ArgEscape, NoEscape に分類, 到達可能なノードに
伝播する。
// comment in escape.hpp
// flags: PrintEscapeAnalysis PrintEliminateAllocations
82
83. Optimize
class ConnectionGraph: public
ResourceObj
// escape state of a node
PointsToNode::EscapeState escape_state(Node *n);
// other information we have collected
bool is_scalar_replaceable(Node *n) {
if (_collecting || (n->_idx >= nodes_size()))
return false;
PointsToNode* ptn = ptnode_adr(n->_idx);
return ptn->escape_state() == PointsToNode::NoEscape
&& ptn->_scalar_replaceable;
}
83