Created
April 26, 2012 11:37
-
-
Save rednaxelafx/2499019 to your computer and use it in GitHub Desktop.
C2 should recognize "obj.getClass() == A.class" code pattern. Diff against jdk8/jdk8/hotspot tip. Previous versions: https://gist.github.com/2481025 , newer version: https://gist.github.com/2622617
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -r dce0525b7ee5 src/share/vm/opto/parse2.cpp | |
--- a/src/share/vm/opto/parse2.cpp Thu Apr 19 12:18:46 2012 -0700 | |
+++ b/src/share/vm/opto/parse2.cpp Thu Apr 26 19:34:51 2012 +0800 | |
@@ -1165,6 +1165,34 @@ | |
} | |
} | |
+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, "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); | |
+ const TypePtr* tp = gvn->type(obj)->is_ptr(); | |
+ if ( !obj // is adr an AddP node? | |
+ || !tp // is obj of pointer type? | |
+ || !(tp->isa_instptr() || tp->isa_aryptr()) // is obj a Java object ptr? | |
+ || off != oopDesc::klass_offset_in_bytes()) // loading oopDesc::_klass? | |
+ return NULL; | |
+ | |
+ return obj; | |
+} | |
+ | |
//----------------------------adjust_map_after_if------------------------------ | |
// Adjust the JVM state to reflect the result of taking this path. | |
// Basically, it means inspecting the CmpNode controlling this | |
@@ -1234,6 +1262,28 @@ | |
return; | |
+ Node* obj = extract_obj_from_klass_load(&_gvn, val); | |
+ const TypeKlassPtr* tkp = tcon->isa_klassptr(); | |
+ if (obj && tkp && btest == BoolTest::eq) { | |
+ const TypeOopPtr* obj_xtype = tkp->as_instance_type(); | |
+ if (obj_xtype->isa_instptr() || obj_xtype->isa_aryptr()) { | |
+ // Found: | |
+ // Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq]) | |
+ // or the narrowOop equivalent. | |
+ // obj has to be of the exact type Foo if the CmpP succeeds. | |
+ assert(obj_xtype->higher_equal(_gvn.type(obj)), "must improve"); | |
+ assert(obj_xtype->klass_is_exact(), "klass should be exact"); | |
+ | |
+ CheckCastPPNode* ccast = new (C, 2) CheckCastPPNode(control(), obj, obj_xtype); | |
+ // Delay transform() call to allow recovery of pre-cast value | |
+ // at the control merge. | |
+ _gvn.set_type_bottom(ccast); | |
+ record_for_igvn(ccast); | |
+ replace_in_map(obj, ccast); | |
+ return; | |
+ } | |
+ } | |
+ | |
int val_in_map = map()->find_edge(val); | |
if (val_in_map < 0) return; // replace_in_map would be useless | |
{ | |
diff -r dce0525b7ee5 src/share/vm/opto/subnode.cpp | |
--- a/src/share/vm/opto/subnode.cpp Thu Apr 19 12:18:46 2012 -0700 | |
+++ b/src/share/vm/opto/subnode.cpp Thu Apr 26 19:34:51 2012 +0800 | |
@@ -702,12 +702,75 @@ | |
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* ldk = AddPNode::Ideal_base_and_offset(adr, phase, off); | |
+ const TypeKlassPtr* tkp = phase->type(ldk)->isa_klassptr(); | |
+ if (!tkp || off != in_bytes(Klass::java_mirror_offset())) return NULL; | |
+ | |
+ // now we've found the klass node of a Java mirror load | |
+ return ldk; | |
+} | |
+ | |
+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(); | |
+ if ( !mirror_type || !mirror_type->is_klass() | |
+ || mirror_type->is_classless()) | |
+ return NULL; | |
+ | |
+ return phase->makecon(TypeKlassPtr::make((ciKlass*) mirror_type)); | |
+} | |
+ | |
//------------------------------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* ldk1 = isa_java_mirror_load(phase, in(1)); | |
+ Node* ldk2 = isa_java_mirror_load(phase, in(2)); | |
+ Node* conk2 = isa_const_java_mirror(phase, in(2)); | |
+ | |
+ if (ldk1 && (ldk2 || conk2)) { | |
+ Node* lhs = ldk1; | |
+ Node* rhs = ldk2 != NULL ? ldk2 : 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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -r dce0525b7ee5 src/share/vm/opto/parse2.cpp | |
--- a/src/share/vm/opto/parse2.cpp Thu Apr 19 12:18:46 2012 -0700 | |
+++ b/src/share/vm/opto/parse2.cpp Fri Apr 27 21:05:32 2012 +0800 | |
@@ -1165,6 +1165,34 @@ | |
} | |
} | |
+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, "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); | |
+ const TypePtr* tp = gvn->type(obj)->is_ptr(); | |
+ if ( !obj // is adr an AddP node? | |
+ || !tp // is obj of pointer type? | |
+ || !(tp->isa_instptr() || tp->isa_aryptr()) // is obj a Java object ptr? | |
+ || off != oopDesc::klass_offset_in_bytes()) // loading oopDesc::_klass? | |
+ return NULL; | |
+ | |
+ return obj; | |
+} | |
+ | |
//----------------------------adjust_map_after_if------------------------------ | |
// Adjust the JVM state to reflect the result of taking this path. | |
// Basically, it means inspecting the CmpNode controlling this | |
@@ -1234,6 +1262,28 @@ | |
return; | |
+ Node* obj = extract_obj_from_klass_load(&_gvn, val); | |
+ const TypeKlassPtr* tkp = tcon->isa_klassptr(); | |
+ if (obj && tkp && btest == BoolTest::eq) { | |
+ const TypeOopPtr* obj_xtype = tkp->as_instance_type(); | |
+ if (obj_xtype->isa_instptr() || obj_xtype->isa_aryptr()) { | |
+ // Found: | |
+ // Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq]) | |
+ // or the narrowOop equivalent. | |
+ // obj has to be of the exact type Foo if the CmpP succeeds. | |
+ assert(obj_xtype->higher_equal(_gvn.type(obj)), "must improve"); | |
+ assert(obj_xtype->klass_is_exact(), "klass should be exact"); | |
+ | |
+ CheckCastPPNode* ccast = new (C, 2) CheckCastPPNode(control(), obj, obj_xtype); | |
+ // Delay transform() call to allow recovery of pre-cast value | |
+ // at the control merge. | |
+ _gvn.set_type_bottom(ccast); | |
+ record_for_igvn(ccast); | |
+ replace_in_map(obj, ccast); | |
+ return; | |
+ } | |
+ } | |
+ | |
int val_in_map = map()->find_edge(val); | |
if (val_in_map < 0) return; // replace_in_map would be useless | |
{ | |
diff -r dce0525b7ee5 src/share/vm/opto/subnode.cpp | |
--- a/src/share/vm/opto/subnode.cpp Thu Apr 19 12:18:46 2012 -0700 | |
+++ b/src/share/vm/opto/subnode.cpp Fri Apr 27 21:05:32 2012 +0800 | |
@@ -702,12 +702,83 @@ | |
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); | |
+ const TypeKlassPtr* tkp = phase->type(k)->isa_klassptr(); | |
+ if (!tkp || off != in_bytes(Klass::java_mirror_offset())) return NULL; | |
+ | |
+ // now 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()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment