# HG changeset patch # User kvn # Date 1337957591 25200 # Node ID 8f6ce6f1049ba33cc3dc46badded000de9a30123 # Parent 7089278210e2f766147b09086d4b287076cc5d7a 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 diff -r 7089278210e2 -r 8f6ce6f1049b src/share/vm/opto/parse.hpp --- a/src/share/vm/opto/parse.hpp Thu May 24 18:39:44 2012 -0700 +++ b/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); diff -r 7089278210e2 -r 8f6ce6f1049b src/share/vm/opto/parse2.cpp --- a/src/share/vm/opto/parse2.cpp Thu May 24 18:39:44 2012 -0700 +++ b/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. diff -r 7089278210e2 -r 8f6ce6f1049b src/share/vm/opto/subnode.cpp --- a/src/share/vm/opto/subnode.cpp Thu May 24 18:39:44 2012 -0700 +++ b/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())