7170463: C2 should recognize "obj.getClass() == A.class" code pattern

Fri, 25 May 2012 07:53:11 -0700

author
kvn
date
Fri, 25 May 2012 07:53:11 -0700
changeset 3834
8f6ce6f1049b
parent 3791
7089278210e2
child 3835
4d8787136e08

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>

src/share/vm/opto/parse.hpp file | annotate | diff | comparison | revisions
src/share/vm/opto/parse2.cpp file | annotate | diff | comparison | revisions
src/share/vm/opto/subnode.cpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/opto/parse.hpp	Thu May 24 18:39:44 2012 -0700
     1.2 +++ b/src/share/vm/opto/parse.hpp	Fri May 25 07:53:11 2012 -0700
     1.3 @@ -527,6 +527,9 @@
     1.4    int     repush_if_args();
     1.5    void    adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
     1.6                                Block* path, Block* other_path);
     1.7 +  void    sharpen_type_after_if(BoolTest::mask btest,
     1.8 +                                Node* con, const Type* tcon,
     1.9 +                                Node* val, const Type* tval);
    1.10    IfNode* jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask);
    1.11    Node*   jump_if_join(Node* iffalse, Node* iftrue);
    1.12    void    jump_if_true_fork(IfNode *ifNode, int dest_bci_if_true, int prof_table_index);
     2.1 --- a/src/share/vm/opto/parse2.cpp	Thu May 24 18:39:44 2012 -0700
     2.2 +++ b/src/share/vm/opto/parse2.cpp	Fri May 25 07:53:11 2012 -0700
     2.3 @@ -1233,6 +1233,71 @@
     2.4    if (!have_con)                        // remaining adjustments need a con
     2.5      return;
     2.6  
     2.7 +  sharpen_type_after_if(btest, con, tcon, val, tval);
     2.8 +}
     2.9 +
    2.10 +
    2.11 +static Node* extract_obj_from_klass_load(PhaseGVN* gvn, Node* n) {
    2.12 +  Node* ldk;
    2.13 +  if (n->is_DecodeN()) {
    2.14 +    if (n->in(1)->Opcode() != Op_LoadNKlass) {
    2.15 +      return NULL;
    2.16 +    } else {
    2.17 +      ldk = n->in(1);
    2.18 +    }
    2.19 +  } else if (n->Opcode() != Op_LoadKlass) {
    2.20 +    return NULL;
    2.21 +  } else {
    2.22 +    ldk = n;
    2.23 +  }
    2.24 +  assert(ldk != NULL && ldk->is_Load(), "should have found a LoadKlass or LoadNKlass node");
    2.25 +
    2.26 +  Node* adr = ldk->in(MemNode::Address);
    2.27 +  intptr_t off = 0;
    2.28 +  Node* obj = AddPNode::Ideal_base_and_offset(adr, gvn, off);
    2.29 +  if (obj == NULL || off != oopDesc::klass_offset_in_bytes()) // loading oopDesc::_klass?
    2.30 +    return NULL;
    2.31 +  const TypePtr* tp = gvn->type(obj)->is_ptr();
    2.32 +  if (tp == NULL || !(tp->isa_instptr() || tp->isa_aryptr())) // is obj a Java object ptr?
    2.33 +    return NULL;
    2.34 +
    2.35 +  return obj;
    2.36 +}
    2.37 +
    2.38 +void Parse::sharpen_type_after_if(BoolTest::mask btest,
    2.39 +                                  Node* con, const Type* tcon,
    2.40 +                                  Node* val, const Type* tval) {
    2.41 +  // Look for opportunities to sharpen the type of a node
    2.42 +  // whose klass is compared with a constant klass.
    2.43 +  if (btest == BoolTest::eq && tcon->isa_klassptr()) {
    2.44 +    Node* obj = extract_obj_from_klass_load(&_gvn, val);
    2.45 +    const TypeOopPtr* con_type = tcon->isa_klassptr()->as_instance_type();
    2.46 +    if (obj != NULL && (con_type->isa_instptr() || con_type->isa_aryptr())) {
    2.47 +       // Found:
    2.48 +       //   Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq])
    2.49 +       // or the narrowOop equivalent.
    2.50 +       const Type* obj_type = _gvn.type(obj);
    2.51 +       const TypeOopPtr* tboth = obj_type->join(con_type)->isa_oopptr();
    2.52 +       if (tboth != NULL && tboth != obj_type && tboth->higher_equal(obj_type)) {
    2.53 +          // obj has to be of the exact type Foo if the CmpP succeeds.
    2.54 +          assert(tboth->klass_is_exact(), "klass should be exact");
    2.55 +          int obj_in_map = map()->find_edge(obj);
    2.56 +          JVMState* jvms = this->jvms();
    2.57 +          if (obj_in_map >= 0 &&
    2.58 +              (jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) {
    2.59 +            TypeNode* ccast = new (C, 2) CheckCastPPNode(control(), obj, tboth);
    2.60 +            const Type* tcc = ccast->as_Type()->type();
    2.61 +            assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve");
    2.62 +            // Delay transform() call to allow recovery of pre-cast value
    2.63 +            // at the control merge.
    2.64 +            _gvn.set_type_bottom(ccast);
    2.65 +            record_for_igvn(ccast);
    2.66 +            // Here's the payoff.
    2.67 +            replace_in_map(obj, ccast);
    2.68 +          }
    2.69 +       }
    2.70 +    }
    2.71 +  }
    2.72  
    2.73    int val_in_map = map()->find_edge(val);
    2.74    if (val_in_map < 0)  return;          // replace_in_map would be useless
    2.75 @@ -1265,6 +1330,7 @@
    2.76          // Exclude tests vs float/double 0 as these could be
    2.77          // either +0 or -0.  Just because you are equal to +0
    2.78          // doesn't mean you ARE +0!
    2.79 +        // Note, following code also replaces Long and Oop values.
    2.80          if ((!tf || tf->_f != 0.0) &&
    2.81              (!td || td->_d != 0.0))
    2.82            cast = con;                   // Replace non-constant val by con.
     3.1 --- a/src/share/vm/opto/subnode.cpp	Thu May 24 18:39:44 2012 -0700
     3.2 +++ b/src/share/vm/opto/subnode.cpp	Fri May 25 07:53:11 2012 -0700
     3.3 @@ -702,12 +702,84 @@
     3.4      return TypeInt::CC;
     3.5  }
     3.6  
     3.7 +static inline Node* isa_java_mirror_load(PhaseGVN* phase, Node* n) {
     3.8 +  // Return the klass node for
     3.9 +  //   LoadP(AddP(foo:Klass, #java_mirror))
    3.10 +  //   or NULL if not matching.
    3.11 +  if (n->Opcode() != Op_LoadP) return NULL;
    3.12 +
    3.13 +  const TypeInstPtr* tp = phase->type(n)->isa_instptr();
    3.14 +  if (!tp || tp->klass() != phase->C->env()->Class_klass()) return NULL;
    3.15 +
    3.16 +  Node* adr = n->in(MemNode::Address);
    3.17 +  intptr_t off = 0;
    3.18 +  Node* k = AddPNode::Ideal_base_and_offset(adr, phase, off);
    3.19 +  if (k == NULL)  return NULL;
    3.20 +  const TypeKlassPtr* tkp = phase->type(k)->isa_klassptr();
    3.21 +  if (!tkp || off != in_bytes(Klass::java_mirror_offset())) return NULL;
    3.22 +
    3.23 +  // We've found the klass node of a Java mirror load.
    3.24 +  return k;
    3.25 +}
    3.26 +
    3.27 +static inline Node* isa_const_java_mirror(PhaseGVN* phase, Node* n) {
    3.28 +  // for ConP(Foo.class) return ConP(Foo.klass)
    3.29 +  // otherwise return NULL
    3.30 +  if (!n->is_Con()) return NULL;
    3.31 +
    3.32 +  const TypeInstPtr* tp = phase->type(n)->isa_instptr();
    3.33 +  if (!tp) return NULL;
    3.34 +
    3.35 +  ciType* mirror_type = tp->java_mirror_type();
    3.36 +  // TypeInstPtr::java_mirror_type() returns non-NULL for compile-
    3.37 +  // time Class constants only.
    3.38 +  if (!mirror_type) return NULL;
    3.39 +
    3.40 +  // x.getClass() == int.class can never be true (for all primitive types)
    3.41 +  // Return a ConP(NULL) node for this case.
    3.42 +  if (mirror_type->is_classless()) {
    3.43 +    return phase->makecon(TypePtr::NULL_PTR);
    3.44 +  }
    3.45 +
    3.46 +  // return the ConP(Foo.klass)
    3.47 +  assert(mirror_type->is_klass(), "mirror_type should represent a klassOop");
    3.48 +  return phase->makecon(TypeKlassPtr::make(mirror_type->as_klass()));
    3.49 +}
    3.50 +
    3.51  //------------------------------Ideal------------------------------------------
    3.52 -// Check for the case of comparing an unknown klass loaded from the primary
    3.53 +// Normalize comparisons between Java mirror loads to compare the klass instead.
    3.54 +//
    3.55 +// Also check for the case of comparing an unknown klass loaded from the primary
    3.56  // super-type array vs a known klass with no subtypes.  This amounts to
    3.57  // checking to see an unknown klass subtypes a known klass with no subtypes;
    3.58  // this only happens on an exact match.  We can shorten this test by 1 load.
    3.59  Node *CmpPNode::Ideal( PhaseGVN *phase, bool can_reshape ) {
    3.60 +  // Normalize comparisons between Java mirrors into comparisons of the low-
    3.61 +  // level klass, where a dependent load could be shortened.
    3.62 +  //
    3.63 +  // The new pattern has a nice effect of matching the same pattern used in the
    3.64 +  // fast path of instanceof/checkcast/Class.isInstance(), which allows
    3.65 +  // redundant exact type check be optimized away by GVN.
    3.66 +  // For example, in
    3.67 +  //   if (x.getClass() == Foo.class) {
    3.68 +  //     Foo foo = (Foo) x;
    3.69 +  //     // ... use a ...
    3.70 +  //   }
    3.71 +  // a CmpPNode could be shared between if_acmpne and checkcast
    3.72 +  {
    3.73 +    Node* k1 = isa_java_mirror_load(phase, in(1));
    3.74 +    Node* k2 = isa_java_mirror_load(phase, in(2));
    3.75 +    Node* conk2 = isa_const_java_mirror(phase, in(2));
    3.76 +
    3.77 +    if (k1 && (k2 || conk2)) {
    3.78 +      Node* lhs = k1;
    3.79 +      Node* rhs = (k2 != NULL) ? k2 : conk2;
    3.80 +      this->set_req(1, lhs);
    3.81 +      this->set_req(2, rhs);
    3.82 +      return this;
    3.83 +    }
    3.84 +  }
    3.85 +
    3.86    // Constant pointer on right?
    3.87    const TypeKlassPtr* t2 = phase->type(in(2))->isa_klassptr();
    3.88    if (t2 == NULL || !t2->klass_is_exact())

mercurial