changeset 8796:6ad64d95053d

8004073: Implement C2 Ideal node specific dump() method Summary: add Node::dump_rel() to dump a node and its related nodes (the notion of "related" depends on the node at hand); add Node::dump_comp() to dump a node in compact representation; add Node::dump_rel_comp() to dump a node and its related nodes in compact representation; add the required machinery; extend some C2 IR nodes with compact and related dumping Reviewed-by: kvn, roland
author mhaupt
date Wed, 18 Mar 2015 16:16:30 +0100
parents 9605c3005f20
children 1659894de94f
files src/share/vm/opto/arraycopynode.cpp src/share/vm/opto/arraycopynode.hpp src/share/vm/opto/callnode.cpp src/share/vm/opto/callnode.hpp src/share/vm/opto/cfgnode.cpp src/share/vm/opto/cfgnode.hpp src/share/vm/opto/ifnode.cpp src/share/vm/opto/movenode.cpp src/share/vm/opto/movenode.hpp src/share/vm/opto/multnode.cpp src/share/vm/opto/multnode.hpp src/share/vm/opto/node.cpp src/share/vm/opto/node.hpp src/share/vm/opto/rootnode.cpp src/share/vm/opto/rootnode.hpp src/share/vm/opto/subnode.cpp src/share/vm/opto/subnode.hpp
diffstat 17 files changed, 593 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/opto/arraycopynode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/arraycopynode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -79,10 +79,15 @@
 
 #ifndef PRODUCT
 const char* ArrayCopyNode::_kind_names[] = {"arraycopy", "arraycopy, validated arguments", "clone", "oop array clone", "CopyOf", "CopyOfRange"};
+
 void ArrayCopyNode::dump_spec(outputStream *st) const {
   CallNode::dump_spec(st);
   st->print(" (%s%s)", _kind_names[_kind], _alloc_tightly_coupled ? ", tightly coupled allocation" : "");
 }
+
+void ArrayCopyNode::dump_compact_spec(outputStream* st) const {
+  st->print("%s%s", _kind_names[_kind], _alloc_tightly_coupled ? ",tight" : "");
+}
 #endif
 
 intptr_t ArrayCopyNode::get_length_if_constant(PhaseGVN *phase) const {
--- a/src/share/vm/opto/arraycopynode.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/arraycopynode.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -164,6 +164,7 @@
 
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
+  virtual void dump_compact_spec(outputStream* st) const;
 #endif
 };
 #endif // SHARE_VM_OPTO_ARRAYCOPYNODE_HPP
--- a/src/share/vm/opto/callnode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/callnode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -52,6 +52,7 @@
 const Type *StartNode::Value(PhaseTransform *phase) const { return _domain; }
 #ifndef PRODUCT
 void StartNode::dump_spec(outputStream *st) const { st->print(" #"); _domain->dump_on(st);}
+void StartNode::dump_compact_spec(outputStream *st) const { /* empty */ }
 #endif
 
 //------------------------------Ideal------------------------------------------
@@ -121,6 +122,23 @@
     if( !Verbose && !WizardMode )   bottom_type()->dump_on(st);
   }
 }
+
+void ParmNode::dump_compact_spec(outputStream *st) const {
+  if (_con < TypeFunc::Parms) {
+    st->print("%s", names[_con]);
+  } else {
+    st->print("%d:", _con-TypeFunc::Parms);
+    // unconditionally dump bottom_type
+    bottom_type()->dump_on(st);
+  }
+}
+
+// For a ParmNode, all immediate inputs and outputs are considered relevant
+// both in compact and standard representation.
+void ParmNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  this->collect_nodes(in_rel, 1, false, false);
+  this->collect_nodes(out_rel, -1, false, false);
+}
 #endif
 
 uint ParmNode::ideal_reg() const {
@@ -948,6 +966,14 @@
   if( _method ) _method->print_short_name(st);
   CallNode::dump_spec(st);
 }
+
+void CallJavaNode::dump_compact_spec(outputStream* st) const {
+  if (_method) {
+    _method->print_short_name(st);
+  } else {
+    st->print("<?>");
+  }
+}
 #endif
 
 //=============================================================================
@@ -995,6 +1021,16 @@
   }
   CallJavaNode::dump_spec(st);
 }
+
+void CallStaticJavaNode::dump_compact_spec(outputStream* st) const {
+  if (_method) {
+    _method->print_short_name(st);
+  } else if (_name) {
+    st->print("%s", _name);
+  } else {
+    st->print("<?>");
+  }
+}
 #endif
 
 //=============================================================================
@@ -1130,6 +1166,19 @@
   st->print(" SafePoint ");
   _replaced_nodes.dump(st);
 }
+
+// The related nodes of a SafepointNode are all data inputs, excluding the
+// control boundary, as well as all outputs till level 2 (to include projection
+// nodes and targets). In compact mode, just include inputs till level 1 and
+// outputs as before.
+void SafePointNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  if (compact) {
+    this->collect_nodes(in_rel, 1, false, false);
+  } else {
+    this->collect_nodes_in_all_data(in_rel, false);
+  }
+  this->collect_nodes(out_rel, -2, false, false);
+}
 #endif
 
 const RegMask &SafePointNode::in_RegMask(uint idx) const {
@@ -1676,6 +1725,27 @@
     _counter->set_tag(NamedCounter::EliminatedLockCounter);
   }
 }
+
+const char* AbstractLockNode::_kind_names[] = {"Regular", "NonEscObj", "Coarsened", "Nested"};
+
+void AbstractLockNode::dump_spec(outputStream* st) const {
+  st->print("%s ", _kind_names[_kind]);
+  CallNode::dump_spec(st);
+}
+
+void AbstractLockNode::dump_compact_spec(outputStream* st) const {
+  st->print("%s", _kind_names[_kind]);
+}
+
+// The related set of lock nodes includes the control boundary.
+void AbstractLockNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  if (compact) {
+      this->collect_nodes(in_rel, 1, false, false);
+    } else {
+      this->collect_nodes_in_all_data(in_rel, true);
+    }
+    this->collect_nodes(out_rel, -2, false, false);
+}
 #endif
 
 //=============================================================================
--- a/src/share/vm/opto/callnode.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/callnode.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -84,6 +84,7 @@
   virtual uint ideal_reg() const { return 0; }
 #ifndef PRODUCT
   virtual void  dump_spec(outputStream *st) const;
+  virtual void  dump_compact_spec(outputStream *st) const;
 #endif
 };
 
@@ -110,6 +111,8 @@
   virtual uint ideal_reg() const;
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
+  virtual void dump_compact_spec(outputStream *st) const;
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
 #endif
 };
 
@@ -476,6 +479,7 @@
 
 #ifndef PRODUCT
   virtual void           dump_spec(outputStream *st) const;
+  virtual void           related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
 #endif
 };
 
@@ -675,6 +679,7 @@
 
 #ifndef PRODUCT
   virtual void  dump_spec(outputStream *st) const;
+  virtual void  dump_compact_spec(outputStream *st) const;
 #endif
 };
 
@@ -730,6 +735,7 @@
   virtual int         Opcode() const;
 #ifndef PRODUCT
   virtual void        dump_spec(outputStream *st) const;
+  virtual void        dump_compact_spec(outputStream *st) const;
 #endif
 };
 
@@ -951,6 +957,7 @@
   } _kind;
 #ifndef PRODUCT
   NamedCounter* _counter;
+  static const char* _kind_names[Nested+1];
 #endif
 
 protected:
@@ -1005,6 +1012,9 @@
 #ifndef PRODUCT
   void create_lock_counter(JVMState* s);
   NamedCounter* counter() const { return _counter; }
+  virtual void dump_spec(outputStream* st) const;
+  virtual void dump_compact_spec(outputStream* st) const;
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
 #endif
 };
 
--- a/src/share/vm/opto/cfgnode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/cfgnode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -2023,6 +2023,14 @@
 }
 
 #ifndef PRODUCT
+void PhiNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  // For a PhiNode, the set of related nodes includes all inputs till level 2,
+  // and all outputs till level 1. In compact mode, inputs till level 1 are
+  // collected.
+  this->collect_nodes(in_rel, compact ? 1 : 2, false, false);
+  this->collect_nodes(out_rel, -1, false, false);
+}
+
 void PhiNode::dump_spec(outputStream *st) const {
   TypeNode::dump_spec(st);
   if (is_tripcount()) {
@@ -2047,11 +2055,33 @@
   return RegMask::Empty;
 }
 
+#ifndef PRODUCT
+//-----------------------------related-----------------------------------------
+// The related nodes of a GotoNode are all inputs at level 1, as well as the
+// outputs at level 1. This is regardless of compact mode.
+void GotoNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  this->collect_nodes(in_rel, 1, false, false);
+  this->collect_nodes(out_rel, -1, false, false);
+}
+#endif
+
+
 //=============================================================================
 const RegMask &JumpNode::out_RegMask() const {
   return RegMask::Empty;
 }
 
+#ifndef PRODUCT
+//-----------------------------related-----------------------------------------
+// The related nodes of a JumpNode are all inputs at level 1, as well as the
+// outputs at level 2 (to include actual jump targets beyond projection nodes).
+// This is regardless of compact mode.
+void JumpNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  this->collect_nodes(in_rel, 1, false, false);
+  this->collect_nodes(out_rel, -2, false, false);
+}
+#endif
+
 //=============================================================================
 const RegMask &JProjNode::out_RegMask() const {
   return RegMask::Empty;
@@ -2105,7 +2135,18 @@
 #ifndef PRODUCT
 void JumpProjNode::dump_spec(outputStream *st) const {
   ProjNode::dump_spec(st);
-   st->print("@bci %d ",_dest_bci);
+  st->print("@bci %d ",_dest_bci);
+}
+
+void JumpProjNode::dump_compact_spec(outputStream *st) const {
+  ProjNode::dump_compact_spec(st);
+  st->print("(%d)%d@%d", _switch_val, _proj_no, _dest_bci);
+}
+
+void JumpProjNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  // The related nodes of a JumpProjNode are its inputs and outputs at level 1.
+  this->collect_nodes(in_rel, 1, false, false);
+  this->collect_nodes(out_rel, -1, false, false);
 }
 #endif
 
--- a/src/share/vm/opto/cfgnode.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/cfgnode.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -204,6 +204,7 @@
   virtual const RegMask &out_RegMask() const;
   virtual const RegMask &in_RegMask(uint) const;
 #ifndef PRODUCT
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
   virtual void dump_spec(outputStream *st) const;
 #endif
 #ifdef ASSERT
@@ -229,6 +230,10 @@
   virtual const Type *Value( PhaseTransform *phase ) const;
   virtual Node *Identity( PhaseTransform *phase );
   virtual const RegMask &out_RegMask() const;
+
+#ifndef PRODUCT
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
+#endif
 };
 
 //------------------------------CProjNode--------------------------------------
@@ -382,6 +387,7 @@
 
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
+  virtual void related(GrowableArray <Node *> *in_rel, GrowableArray <Node *> *out_rel, bool compact) const;
 #endif
 };
 
@@ -393,6 +399,11 @@
 protected:
   // Type of If input when this branch is always taken
   virtual bool always_taken(const TypeTuple* t) const = 0;
+
+#ifndef PRODUCT
+public:
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
+#endif
 };
 
 class IfTrueNode : public IfProjNode {
@@ -455,6 +466,9 @@
   virtual int   Opcode() const;
   virtual const RegMask& out_RegMask() const;
   virtual const Node* is_block_proj() const { return this; }
+#ifndef PRODUCT
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
+#endif
 };
 
 class JumpProjNode : public JProjNode {
@@ -479,6 +493,8 @@
   uint proj_no()     const { return _proj_no; }
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
+  virtual void dump_compact_spec(outputStream *st) const;
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
 #endif
 };
 
--- a/src/share/vm/opto/ifnode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/ifnode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1601,11 +1601,41 @@
   return this;
 }
 
+#ifndef PRODUCT
+//-------------------------------related---------------------------------------
+// An IfProjNode's related node set consists of its input (an IfNode) including
+// the IfNode's condition, plus all of its outputs at level 1. In compact mode,
+// the restrictions for IfNode apply (see IfNode::rel).
+void IfProjNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  Node* ifNode = this->in(0);
+  in_rel->append(ifNode);
+  if (compact) {
+    ifNode->collect_nodes(in_rel, 3, false, true);
+  } else {
+    ifNode->collect_nodes_in_all_data(in_rel, false);
+  }
+  this->collect_nodes(out_rel, -1, false, false);
+}
+
 //------------------------------dump_spec--------------------------------------
-#ifndef PRODUCT
 void IfNode::dump_spec(outputStream *st) const {
   st->print("P=%f, C=%f",_prob,_fcnt);
 }
+
+//-------------------------------related---------------------------------------
+// For an IfNode, the set of related output nodes is just the output nodes till
+// depth 2, i.e, the IfTrue/IfFalse projection nodes plus the nodes they refer.
+// The related input nodes contain no control nodes, but all data nodes
+// pertaining to the condition. In compact mode, the input nodes are collected
+// up to a depth of 3.
+void IfNode::related(GrowableArray <Node *> *in_rel, GrowableArray <Node *> *out_rel, bool compact) const {
+  if (compact) {
+    this->collect_nodes(in_rel, 3, false, true);
+  } else {
+    this->collect_nodes_in_all_data(in_rel, false);
+  }
+  this->collect_nodes(out_rel, -2, false, false);
+}
 #endif
 
 //------------------------------idealize_test----------------------------------
--- a/src/share/vm/opto/movenode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/movenode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -396,3 +396,17 @@
   return TypeLong::make( v.get_jlong() );
 }
 
+#ifndef PRODUCT
+//----------------------------BinaryNode---------------------------------------
+// The set of related nodes for a BinaryNode is all data inputs and all outputs
+// till level 2 (i.e., one beyond the associated CMoveNode). In compact mode,
+// it's the inputs till level 1 and the outputs till level 2.
+void BinaryNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  if (compact) {
+    this->collect_nodes(in_rel, 1, false, true);
+  } else {
+    this->collect_nodes_in_all_data(in_rel, false);
+  }
+  this->collect_nodes(out_rel, -2, false, false);
+}
+#endif
--- a/src/share/vm/opto/movenode.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/movenode.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -145,6 +145,10 @@
   BinaryNode( Node *n1, Node *n2 ) : Node(0,n1,n2) { }
   virtual int Opcode() const;
   virtual uint ideal_reg() const { return 0; }
+
+#ifndef PRODUCT
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
+#endif
 };
 
 
--- a/src/share/vm/opto/multnode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/multnode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -118,6 +118,20 @@
 bool ProjNode::pinned() const { return in(0)->pinned(); }
 #ifndef PRODUCT
 void ProjNode::dump_spec(outputStream *st) const { st->print("#%d",_con); if(_is_io_use) st->print(" (i_o_use)");}
+
+void ProjNode::dump_compact_spec(outputStream *st) const {
+  for (DUIterator i = this->outs(); this->has_out(i); i++) {
+    Node* o = this->out(i);
+    if (NotANode(o)) {
+      st->print("[?]");
+    } else if (o == NULL) {
+      st->print("[_]");
+    } else {
+      st->print("[%d]", o->_idx);
+    }
+  }
+  st->print("#%d", _con);
+}
 #endif
 
 //----------------------------check_con----------------------------------------
--- a/src/share/vm/opto/multnode.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/multnode.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -87,6 +87,7 @@
 
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
+  virtual void dump_compact_spec(outputStream *st) const;
 #endif
 
   // Return uncommon trap call node if proj is for "proj->[region->..]call_uct"
--- a/src/share/vm/opto/node.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/node.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1489,16 +1489,6 @@
 
 #ifndef PRODUCT
 
-//----------------------------NotANode----------------------------------------
-// Used in debugging code to avoid walking across dead or uninitialized edges.
-static inline bool NotANode(const Node* n) {
-  if (n == NULL)                   return true;
-  if (((intptr_t)n & 1) != 0)      return true;  // uninitialized, etc.
-  if (*(address*)n == badAddress)  return true;  // kill by Node::destruct
-  return false;
-}
-
-
 //------------------------------find------------------------------------------
 // Find a neighbor of this Node with the given _idx
 // If idx is negative, find its absolute value, following both _in and _out.
@@ -1636,11 +1626,11 @@
 
 //------------------------------dump------------------------------------------
 // Dump a Node
-void Node::dump(const char* suffix, outputStream *st) const {
+void Node::dump(const char* suffix, bool mark, outputStream *st) const {
   Compile* C = Compile::current();
   bool is_new = C->node_arena()->contains(this);
   C->_in_dump_cnt++;
-  st->print("%c%d\t%s\t=== ", is_new ? ' ' : 'o', _idx, Name());
+  st->print("%c%d%s\t%s\t=== ", is_new ? ' ' : 'o', _idx, mark ? " >" : "", Name());
 
   // Dump the required and precedence inputs
   dump_req(st);
@@ -1760,42 +1750,60 @@
   st->print("]] ");
 }
 
-//------------------------------dump_nodes-------------------------------------
-static void dump_nodes(const Node* start, int d, bool only_ctrl) {
-  Node* s = (Node*)start; // remove const
-  if (NotANode(s)) return;
-
-  uint depth = (uint)ABS(d);
-  int direction = d;
-  Compile* C = Compile::current();
-  GrowableArray <Node *> nstack(C->unique());
-
-  nstack.append(s);
+//----------------------------collect_nodes_i----------------------------------
+// Collects nodes from an Ideal graph, starting from a given start node and
+// moving in a given direction until a certain depth (distance from the start
+// node) is reached. Duplicates are ignored.
+// Arguments:
+//   nstack:        the nodes are collected into this array.
+//   start:         the node at which to start collecting.
+//   direction:     if this is a positive number, collect input nodes; if it is
+//                  a negative number, collect output nodes.
+//   depth:         collect nodes up to this distance from the start node.
+//   include_start: whether to include the start node in the result collection.
+//   only_ctrl:     whether to regard control edges only during traversal.
+//   only_data:     whether to regard data edges only during traversal.
+static void collect_nodes_i(GrowableArray<Node*> *nstack, const Node* start, int direction, uint depth, bool include_start, bool only_ctrl, bool only_data) {
+  Node* s = (Node*) start; // remove const
+  nstack->append(s);
   int begin = 0;
   int end = 0;
   for(uint i = 0; i < depth; i++) {
-    end = nstack.length();
+    end = nstack->length();
     for(int j = begin; j < end; j++) {
-      Node* tp  = nstack.at(j);
+      Node* tp  = nstack->at(j);
       uint limit = direction > 0 ? tp->len() : tp->outcnt();
       for(uint k = 0; k < limit; k++) {
         Node* n = direction > 0 ? tp->in(k) : tp->raw_out(k);
 
         if (NotANode(n))  continue;
         // do not recurse through top or the root (would reach unrelated stuff)
-        if (n->is_Root() || n->is_top())  continue;
+        if (n->is_Root() || n->is_top()) continue;
         if (only_ctrl && !n->is_CFG()) continue;
+        if (only_data && n->is_CFG()) continue;
 
-        bool on_stack = nstack.contains(n);
+        bool on_stack = nstack->contains(n);
         if (!on_stack) {
-          nstack.append(n);
+          nstack->append(n);
         }
       }
     }
     begin = end;
   }
-  end = nstack.length();
-  if (direction > 0) {
+  if (!include_start) {
+    nstack->remove(s);
+  }
+}
+
+//------------------------------dump_nodes-------------------------------------
+static void dump_nodes(const Node* start, int d, bool only_ctrl) {
+  if (NotANode(start)) return;
+
+  GrowableArray <Node *> nstack(Compile::current()->unique());
+  collect_nodes_i(&nstack, start, d, (uint) ABS(d), true, only_ctrl, false);
+
+  int end = nstack.length();
+  if (d > 0) {
     for(int j = end-1; j >= 0; j--) {
       nstack.at(j)->dump();
     }
@@ -1817,6 +1825,221 @@
   dump_nodes(this, d, true);
 }
 
+//-----------------------------dump_compact------------------------------------
+void Node::dump_comp() const {
+  this->dump_comp("\n");
+}
+
+//-----------------------------dump_compact------------------------------------
+// Dump a Node in compact representation, i.e., just print its name and index.
+// Nodes can specify additional specifics to print in compact representation by
+// implementing dump_compact_spec.
+void Node::dump_comp(const char* suffix, outputStream *st) const {
+  Compile* C = Compile::current();
+  C->_in_dump_cnt++;
+  st->print("%s(%d)", Name(), _idx);
+  this->dump_compact_spec(st);
+  if (suffix) {
+    st->print("%s", suffix);
+  }
+  C->_in_dump_cnt--;
+}
+
+//----------------------------dump_related-------------------------------------
+// Dump a Node's related nodes - the notion of "related" depends on the Node at
+// hand and is determined by the implementation of the virtual method rel.
+void Node::dump_related() const {
+  Compile* C = Compile::current();
+  GrowableArray <Node *> in_rel(C->unique());
+  GrowableArray <Node *> out_rel(C->unique());
+  this->related(&in_rel, &out_rel, false);
+  for (int i = in_rel.length() - 1; i >= 0; i--) {
+    in_rel.at(i)->dump();
+  }
+  this->dump("\n", true);
+  for (int i = 0; i < out_rel.length(); i++) {
+    out_rel.at(i)->dump();
+  }
+}
+
+//----------------------------dump_related-------------------------------------
+// Dump a Node's related nodes up to a given depth (distance from the start
+// node).
+// Arguments:
+//   d_in:  depth for input nodes.
+//   d_out: depth for output nodes (note: this also is a positive number).
+void Node::dump_related(uint d_in, uint d_out) const {
+  Compile* C = Compile::current();
+  GrowableArray <Node *> in_rel(C->unique());
+  GrowableArray <Node *> out_rel(C->unique());
+
+  // call collect_nodes_i directly
+  collect_nodes_i(&in_rel, this, 1, d_in, false, false, false);
+  collect_nodes_i(&out_rel, this, -1, d_out, false, false, false);
+
+  for (int i = in_rel.length() - 1; i >= 0; i--) {
+    in_rel.at(i)->dump();
+  }
+  this->dump("\n", true);
+  for (int i = 0; i < out_rel.length(); i++) {
+    out_rel.at(i)->dump();
+  }
+}
+
+//------------------------dump_related_compact---------------------------------
+// Dump a Node's related nodes in compact representation. The notion of
+// "related" depends on the Node at hand and is determined by the implementation
+// of the virtual method rel.
+void Node::dump_related_compact() const {
+  Compile* C = Compile::current();
+  GrowableArray <Node *> in_rel(C->unique());
+  GrowableArray <Node *> out_rel(C->unique());
+  this->related(&in_rel, &out_rel, true);
+  int n_in = in_rel.length();
+  int n_out = out_rel.length();
+
+  this->dump_comp(n_in == 0 ? "\n" : "  ");
+  for (int i = 0; i < n_in; i++) {
+    in_rel.at(i)->dump_comp(i == n_in - 1 ? "\n" : "  ");
+  }
+  for (int i = 0; i < n_out; i++) {
+    out_rel.at(i)->dump_comp(i == n_out - 1 ? "\n" : "  ");
+  }
+}
+
+//------------------------------related----------------------------------------
+// Collect a Node's related nodes. The default behaviour just collects the
+// inputs and outputs at depth 1, including both control and data flow edges,
+// regardless of whether the presentation is compact or not. For data nodes,
+// the default is to collect all data inputs (till level 1 if compact), and
+// outputs till level 1.
+void Node::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  if (this->is_CFG()) {
+    collect_nodes_i(in_rel, this, 1, 1, false, false, false);
+    collect_nodes_i(out_rel, this, -1, 1, false, false, false);
+  } else {
+    if (compact) {
+      this->collect_nodes(in_rel, 1, false, true);
+    } else {
+      this->collect_nodes_in_all_data(in_rel, false);
+    }
+    this->collect_nodes(out_rel, -1, false, false);
+  }
+}
+
+//---------------------------collect_nodes-------------------------------------
+// An entry point to the low-level node collection facility, to start from a
+// given node in the graph. The start node is by default not included in the
+// result.
+// Arguments:
+//   ns:   collect the nodes into this data structure.
+//   d:    the depth (distance from start node) to which nodes should be
+//         collected. A value >0 indicates input nodes, a value <0, output
+//         nodes.
+//   ctrl: include only control nodes.
+//   data: include only data nodes.
+void Node::collect_nodes(GrowableArray<Node*> *ns, int d, bool ctrl, bool data) const {
+  if (ctrl && data) {
+    // ignore nonsensical combination
+    return;
+  }
+  collect_nodes_i(ns, this, d, (uint) ABS(d), false, ctrl, data);
+}
+
+//--------------------------collect_nodes_in-----------------------------------
+static void collect_nodes_in(Node* start, GrowableArray<Node*> *ns, bool primary_is_data, bool collect_secondary) {
+  // The maximum depth is determined using a BFS that visits all primary (data
+  // or control) inputs and increments the depth at each level.
+  uint d_in = 0;
+  GrowableArray<Node*> nodes(Compile::current()->unique());
+  nodes.push(start);
+  int nodes_at_current_level = 1;
+  int n_idx = 0;
+  while (nodes_at_current_level > 0) {
+    // Add all primary inputs reachable from the current level to the list, and
+    // increase the depth if there were any.
+    int nodes_at_next_level = 0;
+    bool nodes_added = false;
+    while (nodes_at_current_level > 0) {
+      nodes_at_current_level--;
+      Node* current = nodes.at(n_idx++);
+      for (uint i = 0; i < current->len(); i++) {
+        Node* n = current->in(i);
+        if (NotANode(n)) {
+          continue;
+        }
+        if ((primary_is_data && n->is_CFG()) || (!primary_is_data && !n->is_CFG())) {
+          continue;
+        }
+        if (!nodes.contains(n)) {
+          nodes.push(n);
+          nodes_added = true;
+          nodes_at_next_level++;
+        }
+      }
+    }
+    if (nodes_added) {
+      d_in++;
+    }
+    nodes_at_current_level = nodes_at_next_level;
+  }
+  start->collect_nodes(ns, d_in, !primary_is_data, primary_is_data);
+  if (collect_secondary) {
+    // Now, iterate over the secondary nodes in ns and add the respective
+    // boundary reachable from them.
+    GrowableArray<Node*> sns(Compile::current()->unique());
+    for (GrowableArrayIterator<Node*> it = ns->begin(); it != ns->end(); ++it) {
+      Node* n = *it;
+      n->collect_nodes(&sns, 1, primary_is_data, !primary_is_data);
+      for (GrowableArrayIterator<Node*> d = sns.begin(); d != sns.end(); ++d) {
+        ns->append_if_missing(*d);
+      }
+      sns.clear();
+    }
+  }
+}
+
+//---------------------collect_nodes_in_all_data-------------------------------
+// Collect the entire data input graph. Include the control boundary if
+// requested.
+// Arguments:
+//   ns:   collect the nodes into this data structure.
+//   ctrl: if true, include the control boundary.
+void Node::collect_nodes_in_all_data(GrowableArray<Node*> *ns, bool ctrl) const {
+  collect_nodes_in((Node*) this, ns, true, ctrl);
+}
+
+//--------------------------collect_nodes_in_all_ctrl--------------------------
+// Collect the entire control input graph. Include the data boundary if
+// requested.
+//   ns:   collect the nodes into this data structure.
+//   data: if true, include the control boundary.
+void Node::collect_nodes_in_all_ctrl(GrowableArray<Node*> *ns, bool data) const {
+  collect_nodes_in((Node*) this, ns, false, data);
+}
+
+//------------------collect_nodes_out_all_ctrl_boundary------------------------
+// Collect the entire output graph until hitting control node boundaries, and
+// include those.
+void Node::collect_nodes_out_all_ctrl_boundary(GrowableArray<Node*> *ns) const {
+  // Perform a BFS and stop at control nodes.
+  GrowableArray<Node*> nodes(Compile::current()->unique());
+  nodes.push((Node*) this);
+  while (nodes.length() > 0) {
+    Node* current = nodes.pop();
+    if (NotANode(current)) {
+      continue;
+    }
+    ns->append_if_missing(current);
+    if (!current->is_CFG()) {
+      for (DUIterator i = current->outs(); current->has_out(i); i++) {
+        nodes.push(current->out(i));
+      }
+    }
+  }
+  ns->remove((Node*) this);
+}
+
 // VERIFICATION CODE
 // For each input edge to a node (ie - for each Use-Def edge), verify that
 // there is a corresponding Def-Use edge.
@@ -2173,6 +2396,11 @@
     st->print(" #"); _type->dump_on(st);
   }
 }
+
+void TypeNode::dump_compact_spec(outputStream *st) const {
+  st->print("#");
+  _type->dump_on(st);
+}
 #endif
 uint TypeNode::hash() const {
   return Node::hash() + _type->hash();
--- a/src/share/vm/opto/node.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/node.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1038,13 +1038,35 @@
   Node* find(int idx) const;         // Search the graph for the given idx.
   Node* find_ctrl(int idx) const;    // Search control ancestors for the given idx.
   void dump() const { dump("\n"); }  // Print this node.
-  void dump(const char* suffix, outputStream *st = tty) const;// Print this node.
+  void dump(const char* suffix, bool mark = false, outputStream *st = tty) const; // Print this node.
   void dump(int depth) const;        // Print this node, recursively to depth d
   void dump_ctrl(int depth) const;   // Print control nodes, to depth d
-  virtual void dump_req(outputStream *st = tty) const;     // Print required-edge info
-  virtual void dump_prec(outputStream *st = tty) const;    // Print precedence-edge info
-  virtual void dump_out(outputStream *st = tty) const;     // Print the output edge info
-  virtual void dump_spec(outputStream *st) const {}; // Print per-node info
+  void dump_comp() const;            // Print this node in compact representation.
+  // Print this node in compact representation.
+  void dump_comp(const char* suffix, outputStream *st = tty) const;
+  virtual void dump_req(outputStream *st = tty) const;    // Print required-edge info
+  virtual void dump_prec(outputStream *st = tty) const;   // Print precedence-edge info
+  virtual void dump_out(outputStream *st = tty) const;    // Print the output edge info
+  virtual void dump_spec(outputStream *st) const {};      // Print per-node info
+  // Print compact per-node info
+  virtual void dump_compact_spec(outputStream *st) const { dump_spec(st); }
+  void dump_related() const;             // Print related nodes (depends on node at hand).
+  // Print related nodes up to given depths for input and output nodes.
+  void dump_related(uint d_in, uint d_out) const;
+  void dump_related_compact() const;     // Print related nodes in compact representation.
+  // Collect related nodes.
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
+  // Collect nodes starting from this node, explicitly including/excluding control and data links.
+  void collect_nodes(GrowableArray<Node*> *ns, int d, bool ctrl, bool data) const;
+
+  // Node collectors, to be used in implementations of Node::rel().
+  // Collect the entire data input graph. Include control inputs if requested.
+  void collect_nodes_in_all_data(GrowableArray<Node*> *ns, bool ctrl) const;
+  // Collect the entire control input graph. Include data inputs if requested.
+  void collect_nodes_in_all_ctrl(GrowableArray<Node*> *ns, bool data) const;
+  // Collect the entire output graph until hitting and including control nodes.
+  void collect_nodes_out_all_ctrl_boundary(GrowableArray<Node*> *ns) const;
+
   void verify_edges(Unique_Node_List &visited); // Verify bi-directional edges
   void verify() const;               // Check Def-Use info for my subgraph
   static void verify_recur(const Node *n, int verify_depth, VectorSet &old_space, VectorSet &new_space);
@@ -1091,6 +1113,20 @@
 #endif
 };
 
+
+#ifndef PRODUCT
+
+// Used in debugging code to avoid walking across dead or uninitialized edges.
+inline bool NotANode(const Node* n) {
+  if (n == NULL)                   return true;
+  if (((intptr_t)n & 1) != 0)      return true;  // uninitialized, etc.
+  if (*(address*)n == badAddress)  return true;  // kill by Node::destruct
+  return false;
+}
+
+#endif
+
+
 //-----------------------------------------------------------------------------
 // Iterators over DU info, and associated Node functions.
 
@@ -1618,6 +1654,7 @@
   virtual       uint  ideal_reg() const;
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
+  virtual void dump_compact_spec(outputStream *st) const;
 #endif
 };
 
--- a/src/share/vm/opto/rootnode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/rootnode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -88,3 +88,18 @@
 const RegMask &HaltNode::out_RegMask() const {
   return RegMask::Empty;
 }
+
+#ifndef PRODUCT
+//-----------------------------related-----------------------------------------
+// Include all control inputs in the related set, and also the input data
+// boundary. In compact mode, include all inputs till level 2. Also include
+// all outputs at level 1.
+void HaltNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  if (compact) {
+    this->collect_nodes(in_rel, 2, false, false);
+  } else {
+    this->collect_nodes_in_all_ctrl(in_rel, true);
+  }
+  this->collect_nodes(out_rel, -1, false, false);
+}
+#endif
--- a/src/share/vm/opto/rootnode.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/rootnode.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -64,6 +64,10 @@
   virtual const RegMask &out_RegMask() const;
   virtual uint ideal_reg() const { return NotAMachineReg; }
   virtual uint match_edge(uint idx) const { return 0; }
+
+#ifndef PRODUCT
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
+#endif
 };
 
 #endif // SHARE_VM_OPTO_ROOTNODE_HPP
--- a/src/share/vm/opto/subnode.cpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/subnode.cpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -498,6 +498,37 @@
   return this;
 }
 
+#ifndef PRODUCT
+//----------------------------related------------------------------------------
+// Related nodes of comparison nodes include all data inputs (until hitting a
+// control boundary) as well as all outputs until and including control nodes
+// as well as their projections. In compact mode, data inputs till depth 1 and
+// all outputs till depth 1 are considered.
+void CmpNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  if (compact) {
+    this->collect_nodes(in_rel, 1, false, true);
+    this->collect_nodes(out_rel, -1, false, false);
+  } else {
+    this->collect_nodes_in_all_data(in_rel, false);
+    this->collect_nodes_out_all_ctrl_boundary(out_rel);
+    // Now, find all control nodes in out_rel, and include their projections
+    // and projection targets (if any) in the result.
+    GrowableArray<Node*> proj(Compile::current()->unique());
+    for (GrowableArrayIterator<Node*> it = out_rel->begin(); it != out_rel->end(); ++it) {
+      Node* n = *it;
+      if (n->is_CFG() && !n->is_Proj()) {
+        // Assume projections and projection targets are found at levels 1 and 2.
+        n->collect_nodes(&proj, -2, false, false);
+        for (GrowableArrayIterator<Node*> p = proj.begin(); p != proj.end(); ++p) {
+          out_rel->append_if_missing(*p);
+        }
+        proj.clear();
+      }
+    }
+  }
+}
+#endif
+
 //=============================================================================
 //------------------------------cmp--------------------------------------------
 // Simplify a CmpI (compare 2 integers) node, based on local information.
@@ -1396,17 +1427,31 @@
   return _test.cc2logical( phase->type( in(1) ) );
 }
 
+#ifndef PRODUCT
 //------------------------------dump_spec--------------------------------------
 // Dump special per-node info
-#ifndef PRODUCT
 void BoolNode::dump_spec(outputStream *st) const {
   st->print("[");
   _test.dump_on(st);
   st->print("]");
 }
+
+//-------------------------------related---------------------------------------
+// A BoolNode's related nodes are all of its data inputs, and all of its
+// outputs until control nodes are hit, which are included. In compact
+// representation, inputs till level 3 and immediate outputs are included.
+void BoolNode::related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const {
+  if (compact) {
+    this->collect_nodes(in_rel, 3, false, true);
+    this->collect_nodes(out_rel, -1, false, false);
+  } else {
+    this->collect_nodes_in_all_data(in_rel, false);
+    this->collect_nodes_out_all_ctrl_boundary(out_rel);
+  }
+}
 #endif
 
-//------------------------------is_counted_loop_exit_test--------------------------------------
+//----------------------is_counted_loop_exit_test------------------------------
 // Returns true if node is used by a counted loop node.
 bool BoolNode::is_counted_loop_exit_test() {
   for( DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++ ) {
--- a/src/share/vm/opto/subnode.hpp	Wed Jul 29 12:33:48 2015 +0200
+++ b/src/share/vm/opto/subnode.hpp	Wed Mar 18 16:16:30 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -60,7 +60,6 @@
   // Supplied function to return the additive identity type.
   // This is returned whenever the subtracts inputs are the same.
   virtual const Type *add_id() const = 0;
-
 };
 
 
@@ -140,6 +139,13 @@
   const Type *add_id() const { return TypeInt::ZERO; }
   const Type *bottom_type() const { return TypeInt::CC; }
   virtual uint ideal_reg() const { return Op_RegFlags; }
+
+#ifndef PRODUCT
+  // CmpNode and subclasses include all data inputs (until hitting a control
+  // boundary) in their related node set, as well as all outputs until and
+  // including eventual control nodes and their projections.
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
+#endif
 };
 
 //------------------------------CmpINode---------------------------------------
@@ -311,6 +317,7 @@
   bool is_counted_loop_exit_test();
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
+  virtual void related(GrowableArray<Node*> *in_rel, GrowableArray<Node*> *out_rel, bool compact) const;
 #endif
 };