changeset 64:b8f5ba577b02

6673473: (Escape Analysis) Add the instance's field information to PhiNode Summary: Avoid an infinite generation of instance's field values Phi nodes. Reviewed-by: never
author kvn
date Thu, 13 Mar 2008 16:31:32 -0700
parents eac007780a58
children 99269dbf4ba8
files src/share/vm/opto/cfgnode.hpp src/share/vm/opto/loopopts.cpp src/share/vm/opto/memnode.cpp src/share/vm/opto/memnode.hpp src/share/vm/opto/type.cpp src/share/vm/opto/type.hpp
diffstat 6 files changed, 166 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/opto/cfgnode.hpp	Thu Mar 13 16:06:34 2008 -0700
+++ b/src/share/vm/opto/cfgnode.hpp	Thu Mar 13 16:31:32 2008 -0700
@@ -110,14 +110,15 @@
 // input in slot 0.
 class PhiNode : public TypeNode {
   const TypePtr* const _adr_type; // non-null only for Type::MEMORY nodes.
+  const int _inst_id;     // Instance id of the memory slice.
+  const int _inst_index;  // Alias index of the instance memory slice.
+  // Array elements references have the same alias_idx but different offset.
+  const int _inst_offset; // Offset of the instance memory slice.
   // Size is bigger to hold the _adr_type field.
   virtual uint hash() const;    // Check the type
   virtual uint cmp( const Node &n ) const;
   virtual uint size_of() const { return sizeof(*this); }
 
-  // Determine a unique non-trivial input, if any.
-  // Ignore casts if it helps.  Return NULL on failure.
-  Node* unique_input(PhaseTransform *phase);
   // Determine if CMoveNode::is_cmove_id can be used at this join point.
   Node* is_cmove_id(PhaseTransform* phase, int true_path);
 
@@ -127,8 +128,16 @@
          Input                  // Input values are [1..len)
   };
 
-  PhiNode( Node *r, const Type *t, const TypePtr* at = NULL )
-    : TypeNode(t,r->req()), _adr_type(at) {
+  PhiNode( Node *r, const Type *t, const TypePtr* at = NULL,
+           const int iid = TypeOopPtr::UNKNOWN_INSTANCE,
+           const int iidx = Compile::AliasIdxTop,
+           const int ioffs = Type::OffsetTop )
+    : TypeNode(t,r->req()),
+      _adr_type(at),
+      _inst_id(iid),
+      _inst_index(iidx),
+      _inst_offset(ioffs)
+  {
     init_class_id(Class_Phi);
     init_req(0, r);
     verify_adr_type();
@@ -152,6 +161,10 @@
     return NULL;  // not a copy!
   }
 
+  // Determine a unique non-trivial input, if any.
+  // Ignore casts if it helps.  Return NULL on failure.
+  Node* unique_input(PhaseTransform *phase);
+
   // Check for a simple dead loop.
   enum LoopSafety { Safe = 0, Unsafe, UnsafeLoop };
   LoopSafety simple_data_loop_check(Node *in) const;
@@ -161,6 +174,18 @@
   virtual int Opcode() const;
   virtual bool pinned() const { return in(0) != 0; }
   virtual const TypePtr *adr_type() const { verify_adr_type(true); return _adr_type; }
+
+  const int inst_id()     const { return _inst_id; }
+  const int inst_index()  const { return _inst_index; }
+  const int inst_offset() const { return _inst_offset; }
+  bool is_same_inst_field(const Type* tp, int id, int index, int offset) {
+    return type()->basic_type() == tp->basic_type() &&
+           inst_id()     == id     &&
+           inst_index()  == index  &&
+           inst_offset() == offset &&
+           type()->higher_equal(tp);
+  }
+
   virtual const Type *Value( PhaseTransform *phase ) const;
   virtual Node *Identity( PhaseTransform *phase );
   virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
--- a/src/share/vm/opto/loopopts.cpp	Thu Mar 13 16:06:34 2008 -0700
+++ b/src/share/vm/opto/loopopts.cpp	Thu Mar 13 16:31:32 2008 -0700
@@ -32,7 +32,18 @@
   int wins = 0;
   assert( !n->is_CFG(), "" );
   assert( region->is_Region(), "" );
-  Node *phi = new (C, region->req()) PhiNode( region, n->bottom_type() );
+
+  const Type* type = n->bottom_type();
+  const TypeOopPtr *t_oop = _igvn.type(n)->isa_oopptr();
+  Node *phi;
+  if( t_oop != NULL && t_oop->is_instance_field() ) {
+    int iid    = t_oop->instance_id();
+    int index  = C->get_alias_index(t_oop);
+    int offset = t_oop->offset();
+    phi = new (C,region->req()) PhiNode(region, type, NULL, iid, index, offset);
+  } else {
+    phi = new (C,region->req()) PhiNode(region, type);
+  }
   uint old_unique = C->unique();
   for( uint i = 1; i < region->req(); i++ ) {
     Node *x;
--- a/src/share/vm/opto/memnode.cpp	Thu Mar 13 16:06:34 2008 -0700
+++ b/src/share/vm/opto/memnode.cpp	Thu Mar 13 16:31:32 2008 -0700
@@ -87,6 +87,58 @@
 
 #endif
 
+static Node *step_through_mergemem(PhaseGVN *phase, MergeMemNode *mmem,  const TypePtr *tp, const TypePtr *adr_check, outputStream *st) {
+  uint alias_idx = phase->C->get_alias_index(tp);
+  Node *mem = mmem;
+#ifdef ASSERT
+  {
+    // Check that current type is consistent with the alias index used during graph construction
+    assert(alias_idx >= Compile::AliasIdxRaw, "must not be a bad alias_idx");
+    bool consistent =  adr_check == NULL || adr_check->empty() ||
+                       phase->C->must_alias(adr_check, alias_idx );
+    // Sometimes dead array references collapse to a[-1], a[-2], or a[-3]
+    if( !consistent && adr_check != NULL && !adr_check->empty() &&
+           tp->isa_aryptr() &&    tp->offset() == Type::OffsetBot &&
+        adr_check->isa_aryptr() && adr_check->offset() != Type::OffsetBot &&
+        ( adr_check->offset() == arrayOopDesc::length_offset_in_bytes() ||
+          adr_check->offset() == oopDesc::klass_offset_in_bytes() ||
+          adr_check->offset() == oopDesc::mark_offset_in_bytes() ) ) {
+      // don't assert if it is dead code.
+      consistent = true;
+    }
+    if( !consistent ) {
+      st->print("alias_idx==%d, adr_check==", alias_idx);
+      if( adr_check == NULL ) {
+        st->print("NULL");
+      } else {
+        adr_check->dump();
+      }
+      st->cr();
+      print_alias_types();
+      assert(consistent, "adr_check must match alias idx");
+    }
+  }
+#endif
+  // TypeInstPtr::NOTNULL+any is an OOP with unknown offset - generally
+  // means an array I have not precisely typed yet.  Do not do any
+  // alias stuff with it any time soon.
+  const TypeOopPtr *tinst = tp->isa_oopptr();
+  if( tp->base() != Type::AnyPtr &&
+      !(tinst &&
+        tinst->klass()->is_java_lang_Object() &&
+        tinst->offset() == Type::OffsetBot) ) {
+    // compress paths and change unreachable cycles to TOP
+    // If not, we can update the input infinitely along a MergeMem cycle
+    // Equivalent code in PhiNode::Ideal
+    Node* m  = phase->transform(mmem);
+    // If tranformed to a MergeMem, get the desired slice
+    // Otherwise the returned node represents memory for every slice
+    mem = (m->is_MergeMem())? m->as_MergeMem()->memory_at(alias_idx) : m;
+    // Update input if it is progress over what we have now
+  }
+  return mem;
+}
+
 //--------------------------Ideal_common---------------------------------------
 // Look for degenerate control and memory inputs.  Bypass MergeMem inputs.
 // Unhook non-raw memories from complete (macro-expanded) initializations.
@@ -119,48 +171,8 @@
   if (mem->is_MergeMem()) {
     MergeMemNode* mmem = mem->as_MergeMem();
     const TypePtr *tp = t_adr->is_ptr();
-    uint alias_idx = phase->C->get_alias_index(tp);
-#ifdef ASSERT
-    {
-      // Check that current type is consistent with the alias index used during graph construction
-      assert(alias_idx >= Compile::AliasIdxRaw, "must not be a bad alias_idx");
-      const TypePtr *adr_t =  adr_type();
-      bool consistent =  adr_t == NULL || adr_t->empty() || phase->C->must_alias(adr_t, alias_idx );
-      // Sometimes dead array references collapse to a[-1], a[-2], or a[-3]
-      if( !consistent && adr_t != NULL && !adr_t->empty() &&
-             tp->isa_aryptr() &&    tp->offset() == Type::OffsetBot &&
-          adr_t->isa_aryptr() && adr_t->offset() != Type::OffsetBot &&
-          ( adr_t->offset() == arrayOopDesc::length_offset_in_bytes() ||
-            adr_t->offset() == oopDesc::klass_offset_in_bytes() ||
-            adr_t->offset() == oopDesc::mark_offset_in_bytes() ) ) {
-        // don't assert if it is dead code.
-        consistent = true;
-      }
-      if( !consistent ) {
-        tty->print("alias_idx==%d, adr_type()==", alias_idx); if( adr_t == NULL ) { tty->print("NULL"); } else { adr_t->dump(); }
-        tty->cr();
-        print_alias_types();
-        assert(consistent, "adr_type must match alias idx");
-      }
-    }
-#endif
-    // TypeInstPtr::NOTNULL+any is an OOP with unknown offset - generally
-    // means an array I have not precisely typed yet.  Do not do any
-    // alias stuff with it any time soon.
-    const TypeInstPtr *tinst = tp->isa_instptr();
-    if( tp->base() != Type::AnyPtr &&
-        !(tinst &&
-          tinst->klass()->is_java_lang_Object() &&
-          tinst->offset() == Type::OffsetBot) ) {
-      // compress paths and change unreachable cycles to TOP
-      // If not, we can update the input infinitely along a MergeMem cycle
-      // Equivalent code in PhiNode::Ideal
-      Node* m  = phase->transform(mmem);
-      // If tranformed to a MergeMem, get the desired slice
-      // Otherwise the returned node represents memory for every slice
-      mem = (m->is_MergeMem())? m->as_MergeMem()->memory_at(alias_idx) : m;
-      // Update input if it is progress over what we have now
-    }
+
+    mem = step_through_mergemem(phase, mmem, tp, adr_type(), tty);
   }
 
   if (mem != old_mem) {
@@ -534,7 +546,10 @@
           const Node* call = adr->in(0);
           if (call->is_CallStaticJava()) {
             const CallStaticJavaNode* call_java = call->as_CallStaticJava();
-            assert(call_java && call_java->method() == NULL, "must be runtime call");
+            const TypeTuple *r = call_java->tf()->range();
+            assert(r->cnt() > TypeFunc::Parms, "must return value");
+            const Type* ret_type = r->field_at(TypeFunc::Parms);
+            assert(ret_type && ret_type->isa_ptr(), "must return pointer");
             // We further presume that this is one of
             // new_instance_Java, new_array_Java, or
             // the like, but do not assert for this.
@@ -732,6 +747,21 @@
   return NULL;
 }
 
+//----------------------is_instance_field_load_with_local_phi------------------
+bool LoadNode::is_instance_field_load_with_local_phi(Node* ctrl) {
+  if( in(MemNode::Memory)->is_Phi() && in(MemNode::Memory)->in(0) == ctrl &&
+      in(MemNode::Address)->is_AddP() ) {
+    const TypeOopPtr* t_oop = in(MemNode::Address)->bottom_type()->isa_oopptr();
+    // Only instances.
+    if( t_oop != NULL && t_oop->is_instance_field() &&
+        t_oop->offset() != Type::OffsetBot &&
+        t_oop->offset() != Type::OffsetTop) {
+      return true;
+    }
+  }
+  return false;
+}
+
 //------------------------------Identity---------------------------------------
 // Loads are identity if previous store is to same address
 Node *LoadNode::Identity( PhaseTransform *phase ) {
@@ -754,6 +784,25 @@
     // usually runs first, producing the singleton type of the Con.)
     return value;
   }
+
+  // Search for an existing data phi which was generated before for the same
+  // instance's field to avoid infinite genertion of phis in a loop.
+  Node *region = mem->in(0);
+  if (is_instance_field_load_with_local_phi(region)) {
+    const TypePtr *addr_t = in(MemNode::Address)->bottom_type()->isa_ptr();
+    int this_index  = phase->C->get_alias_index(addr_t);
+    int this_offset = addr_t->offset();
+    int this_id    = addr_t->is_oopptr()->instance_id();
+    const Type* this_type = bottom_type();
+    for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) {
+      Node* phi = region->fast_out(i);
+      if (phi->is_Phi() && phi != mem &&
+          phi->as_Phi()->is_same_inst_field(this_type, this_id, this_index, this_offset)) {
+        return phi;
+      }
+    }
+  }
+
   return this;
 }
 
@@ -1189,6 +1238,17 @@
       return value->bottom_type();
   }
 
+  const TypeOopPtr *tinst = tp->isa_oopptr();
+  if (tinst != NULL && tinst->is_instance_field()) {
+    // If we have an instance type and our memory input is the
+    // programs's initial memory state, there is no matching store,
+    // so just return a zero of the appropriate type
+    Node *mem = in(MemNode::Memory);
+    if (mem->is_Parm() && mem->in(0)->is_Start()) {
+      assert(mem->as_Parm()->_con == TypeFunc::Memory, "must be memory Parm");
+      return Type::get_zero_type(_type->basic_type());
+    }
+  }
   return _type;
 }
 
@@ -1712,7 +1772,7 @@
   const TypeOopPtr *adr_oop = phase->type(adr)->isa_oopptr();
   if (adr_oop == NULL)
     return false;
-  if (!adr_oop->is_instance())
+  if (!adr_oop->is_instance_field())
     return false; // if not a distinct instance, there may be aliases of the address
   for (DUIterator_Fast imax, i = adr->fast_outs(imax); i < imax; i++) {
     Node *use = adr->fast_out(i);
@@ -3244,7 +3304,7 @@
     }
   }
 
-  assert(verify_sparse(), "please, no dups of base");
+  assert(progress || verify_sparse(), "please, no dups of base");
   return progress;
 }
 
--- a/src/share/vm/opto/memnode.hpp	Thu Mar 13 16:06:34 2008 -0700
+++ b/src/share/vm/opto/memnode.hpp	Thu Mar 13 16:31:32 2008 -0700
@@ -172,6 +172,9 @@
   // Map a load opcode to its corresponding store opcode.
   virtual int store_Opcode() const = 0;
 
+  // Check if the load's memory input is a Phi node with the same control.
+  bool is_instance_field_load_with_local_phi(Node* ctrl);
+
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
 #endif
--- a/src/share/vm/opto/type.cpp	Thu Mar 13 16:06:34 2008 -0700
+++ b/src/share/vm/opto/type.cpp	Thu Mar 13 16:31:32 2008 -0700
@@ -3164,7 +3164,7 @@
     case TopPTR:
       // Compute new klass on demand, do not use tap->_klass
       xk = (tap->_klass_is_exact | this->_klass_is_exact);
-      return make( ptr, const_oop(), tary, lazy_klass, xk, off );
+      return make( ptr, const_oop(), tary, lazy_klass, xk, off, iid );
     case Constant: {
       ciObject* o = const_oop();
       if( _ptr == Constant ) {
@@ -3176,7 +3176,7 @@
         o = tap->const_oop();
       }
       xk = true;
-      return TypeAryPtr::make( ptr, o, tary, tap->_klass, xk, off );
+      return TypeAryPtr::make( ptr, o, tary, tap->_klass, xk, off, iid );
     }
     case NotNull:
     case BotPTR:
@@ -3263,14 +3263,21 @@
     break;
   }
 
-  st->print("*");
+  if( _offset != 0 ) {
+    int header_size = objArrayOopDesc::header_size() * wordSize;
+    if( _offset == OffsetTop )       st->print("+undefined");
+    else if( _offset == OffsetBot )  st->print("+any");
+    else if( _offset < header_size ) st->print("+%d", _offset);
+    else {
+      BasicType basic_elem_type = elem()->basic_type();
+      int array_base = arrayOopDesc::base_offset_in_bytes(basic_elem_type);
+      int elem_size = type2aelembytes(basic_elem_type);
+      st->print("[%d]", (_offset - array_base)/elem_size);
+    }
+  }
+  st->print(" *");
   if (_instance_id != UNKNOWN_INSTANCE)
     st->print(",iid=%d",_instance_id);
-  if( !_offset ) return;
-  if( _offset == OffsetTop )      st->print("+undefined");
-  else if( _offset == OffsetBot ) st->print("+any");
-  else if( _offset < 12 )         st->print("+%d",_offset);
-  else                            st->print("[%d]", (_offset-12)/4 );
 }
 #endif
 
--- a/src/share/vm/opto/type.hpp	Thu Mar 13 16:06:34 2008 -0700
+++ b/src/share/vm/opto/type.hpp	Thu Mar 13 16:31:32 2008 -0700
@@ -686,6 +686,7 @@
   bool klass_is_exact()    const { return _klass_is_exact; }
   bool is_instance()       const { return _instance_id != UNKNOWN_INSTANCE; }
   uint instance_id()       const { return _instance_id; }
+  bool is_instance_field() const { return _instance_id != UNKNOWN_INSTANCE && _offset >= 0; }
 
   virtual intptr_t get_con() const;