7170463: C2 should recognize "obj.getClass() == A.class" code pattern
authorkvn
Fri, 25 May 2012 07:53:11 -0700
changeset 12945 e63d6176cbd1
parent 12743 e1f1f1cc8d2b
child 12946 6007040eb77d
7170463: C2 should recognize "obj.getClass() == A.class" code pattern Summary: optimize this code pattern obj.getClass() == A.class. Reviewed-by: jrose, kvn Contributed-by: Krystal Mok <sajia@taobao.com>
hotspot/src/share/vm/opto/parse.hpp
hotspot/src/share/vm/opto/parse2.cpp
hotspot/src/share/vm/opto/subnode.cpp
--- a/hotspot/src/share/vm/opto/parse.hpp	Thu May 24 18:39:44 2012 -0700
+++ b/hotspot/src/share/vm/opto/parse.hpp	Fri May 25 07:53:11 2012 -0700
@@ -527,6 +527,9 @@
   int     repush_if_args();
   void    adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
                               Block* path, Block* other_path);
+  void    sharpen_type_after_if(BoolTest::mask btest,
+                                Node* con, const Type* tcon,
+                                Node* val, const Type* tval);
   IfNode* jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask);
   Node*   jump_if_join(Node* iffalse, Node* iftrue);
   void    jump_if_true_fork(IfNode *ifNode, int dest_bci_if_true, int prof_table_index);
--- a/hotspot/src/share/vm/opto/parse2.cpp	Thu May 24 18:39:44 2012 -0700
+++ b/hotspot/src/share/vm/opto/parse2.cpp	Fri May 25 07:53:11 2012 -0700
@@ -1233,6 +1233,71 @@
   if (!have_con)                        // remaining adjustments need a con
     return;
 
+  sharpen_type_after_if(btest, con, tcon, val, tval);
+}
+
+
+static Node* extract_obj_from_klass_load(PhaseGVN* gvn, Node* n) {
+  Node* ldk;
+  if (n->is_DecodeN()) {
+    if (n->in(1)->Opcode() != Op_LoadNKlass) {
+      return NULL;
+    } else {
+      ldk = n->in(1);
+    }
+  } else if (n->Opcode() != Op_LoadKlass) {
+    return NULL;
+  } else {
+    ldk = n;
+  }
+  assert(ldk != NULL && ldk->is_Load(), "should have found a LoadKlass or LoadNKlass node");
+
+  Node* adr = ldk->in(MemNode::Address);
+  intptr_t off = 0;
+  Node* obj = AddPNode::Ideal_base_and_offset(adr, gvn, off);
+  if (obj == NULL || off != oopDesc::klass_offset_in_bytes()) // loading oopDesc::_klass?
+    return NULL;
+  const TypePtr* tp = gvn->type(obj)->is_ptr();
+  if (tp == NULL || !(tp->isa_instptr() || tp->isa_aryptr())) // is obj a Java object ptr?
+    return NULL;
+
+  return obj;
+}
+
+void Parse::sharpen_type_after_if(BoolTest::mask btest,
+                                  Node* con, const Type* tcon,
+                                  Node* val, const Type* tval) {
+  // Look for opportunities to sharpen the type of a node
+  // whose klass is compared with a constant klass.
+  if (btest == BoolTest::eq && tcon->isa_klassptr()) {
+    Node* obj = extract_obj_from_klass_load(&_gvn, val);
+    const TypeOopPtr* con_type = tcon->isa_klassptr()->as_instance_type();
+    if (obj != NULL && (con_type->isa_instptr() || con_type->isa_aryptr())) {
+       // Found:
+       //   Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq])
+       // or the narrowOop equivalent.
+       const Type* obj_type = _gvn.type(obj);
+       const TypeOopPtr* tboth = obj_type->join(con_type)->isa_oopptr();
+       if (tboth != NULL && tboth != obj_type && tboth->higher_equal(obj_type)) {
+          // obj has to be of the exact type Foo if the CmpP succeeds.
+          assert(tboth->klass_is_exact(), "klass should be exact");
+          int obj_in_map = map()->find_edge(obj);
+          JVMState* jvms = this->jvms();
+          if (obj_in_map >= 0 &&
+              (jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) {
+            TypeNode* ccast = new (C, 2) CheckCastPPNode(control(), obj, tboth);
+            const Type* tcc = ccast->as_Type()->type();
+            assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve");
+            // Delay transform() call to allow recovery of pre-cast value
+            // at the control merge.
+            _gvn.set_type_bottom(ccast);
+            record_for_igvn(ccast);
+            // Here's the payoff.
+            replace_in_map(obj, ccast);
+          }
+       }
+    }
+  }
 
   int val_in_map = map()->find_edge(val);
   if (val_in_map < 0)  return;          // replace_in_map would be useless
@@ -1265,6 +1330,7 @@
         // Exclude tests vs float/double 0 as these could be
         // either +0 or -0.  Just because you are equal to +0
         // doesn't mean you ARE +0!
+        // Note, following code also replaces Long and Oop values.
         if ((!tf || tf->_f != 0.0) &&
             (!td || td->_d != 0.0))
           cast = con;                   // Replace non-constant val by con.
--- a/hotspot/src/share/vm/opto/subnode.cpp	Thu May 24 18:39:44 2012 -0700
+++ b/hotspot/src/share/vm/opto/subnode.cpp	Fri May 25 07:53:11 2012 -0700
@@ -702,12 +702,84 @@
     return TypeInt::CC;
 }
 
+static inline Node* isa_java_mirror_load(PhaseGVN* phase, Node* n) {
+  // Return the klass node for
+  //   LoadP(AddP(foo:Klass, #java_mirror))
+  //   or NULL if not matching.
+  if (n->Opcode() != Op_LoadP) return NULL;
+
+  const TypeInstPtr* tp = phase->type(n)->isa_instptr();
+  if (!tp || tp->klass() != phase->C->env()->Class_klass()) return NULL;
+
+  Node* adr = n->in(MemNode::Address);
+  intptr_t off = 0;
+  Node* k = AddPNode::Ideal_base_and_offset(adr, phase, off);
+  if (k == NULL)  return NULL;
+  const TypeKlassPtr* tkp = phase->type(k)->isa_klassptr();
+  if (!tkp || off != in_bytes(Klass::java_mirror_offset())) return NULL;
+
+  // We've found the klass node of a Java mirror load.
+  return k;
+}
+
+static inline Node* isa_const_java_mirror(PhaseGVN* phase, Node* n) {
+  // for ConP(Foo.class) return ConP(Foo.klass)
+  // otherwise return NULL
+  if (!n->is_Con()) return NULL;
+
+  const TypeInstPtr* tp = phase->type(n)->isa_instptr();
+  if (!tp) return NULL;
+
+  ciType* mirror_type = tp->java_mirror_type();
+  // TypeInstPtr::java_mirror_type() returns non-NULL for compile-
+  // time Class constants only.
+  if (!mirror_type) return NULL;
+
+  // x.getClass() == int.class can never be true (for all primitive types)
+  // Return a ConP(NULL) node for this case.
+  if (mirror_type->is_classless()) {
+    return phase->makecon(TypePtr::NULL_PTR);
+  }
+
+  // return the ConP(Foo.klass)
+  assert(mirror_type->is_klass(), "mirror_type should represent a klassOop");
+  return phase->makecon(TypeKlassPtr::make(mirror_type->as_klass()));
+}
+
 //------------------------------Ideal------------------------------------------
-// Check for the case of comparing an unknown klass loaded from the primary
+// Normalize comparisons between Java mirror loads to compare the klass instead.
+//
+// Also check for the case of comparing an unknown klass loaded from the primary
 // super-type array vs a known klass with no subtypes.  This amounts to
 // checking to see an unknown klass subtypes a known klass with no subtypes;
 // this only happens on an exact match.  We can shorten this test by 1 load.
 Node *CmpPNode::Ideal( PhaseGVN *phase, bool can_reshape ) {
+  // Normalize comparisons between Java mirrors into comparisons of the low-
+  // level klass, where a dependent load could be shortened.
+  //
+  // The new pattern has a nice effect of matching the same pattern used in the
+  // fast path of instanceof/checkcast/Class.isInstance(), which allows
+  // redundant exact type check be optimized away by GVN.
+  // For example, in
+  //   if (x.getClass() == Foo.class) {
+  //     Foo foo = (Foo) x;
+  //     // ... use a ...
+  //   }
+  // a CmpPNode could be shared between if_acmpne and checkcast
+  {
+    Node* k1 = isa_java_mirror_load(phase, in(1));
+    Node* k2 = isa_java_mirror_load(phase, in(2));
+    Node* conk2 = isa_const_java_mirror(phase, in(2));
+
+    if (k1 && (k2 || conk2)) {
+      Node* lhs = k1;
+      Node* rhs = (k2 != NULL) ? k2 : conk2;
+      this->set_req(1, lhs);
+      this->set_req(2, rhs);
+      return this;
+    }
+  }
+
   // Constant pointer on right?
   const TypeKlassPtr* t2 = phase->type(in(2))->isa_klassptr();
   if (t2 == NULL || !t2->klass_is_exact())