Created
April 24, 2012 16:04
-
-
Save rednaxelafx/2481025 to your computer and use it in GitHub Desktop.
C2 should recognize "obj.getClass() == A.class" code pattern. Diff against jdk8/jdk8/hotspot tip. New version: https://gist.github.com/2499019
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/subnode.cpp | |
--- a/src/share/vm/opto/subnode.cpp Thu Apr 19 12:18:46 2012 -0700 | |
+++ b/src/share/vm/opto/subnode.cpp Wed Apr 25 00:03:52 2012 +0800 | |
@@ -703,11 +703,69 @@ | |
} | |
//------------------------------Ideal------------------------------------------ | |
-// Check for the case of comparing an unknown klass loaded from the primary | |
+// Check for the case of comparing a Java mirror returned from getClass() vs | |
+// a constant Java mirror. Simplify the pattern 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. | |
+static inline bool is_java_mirror_ptr(const TypeOopPtr* ptr, PhaseGVN* phase) { | |
+ return ptr && ptr->klass_is_exact() && ptr->klass() == phase->C->env()->Class_klass(); | |
+} | |
+ | |
Node *CmpPNode::Ideal( PhaseGVN *phase, bool can_reshape ) { | |
+ // Simplify the code pattern | |
+ // x.getClass() == A.class | |
+ // into | |
+ // x._klass == klassof(A) | |
+ // 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() == A.class) { | |
+ // A a = (A) a; | |
+ // // ... use a ... | |
+ // } | |
+ // a CmpPNode could be shared between if_acmpne and checkcast | |
+ if (in(1)->Opcode() == Op_LoadP && | |
+ in(2)->Opcode() == Op_ConP) { | |
+ Node* ldp1 = in(1); | |
+ const TypeOopPtr* t1 = phase->type(ldp1)->isa_oopptr(); | |
+ const TypeOopPtr* t2 = phase->type(in(2))->isa_oopptr(); | |
+ | |
+ bool match = is_java_mirror_ptr(t1, phase) && is_java_mirror_ptr(t2, phase); | |
+ if (match) { | |
+ Node* adr1 = ldp1->in(MemNode::Address); | |
+ intptr_t off = 0; | |
+ Node* ldk2 = AddPNode::Ideal_base_and_offset(adr1, phase, off); | |
+ | |
+ ciType* mirror_type = ((ciInstance*) t2->const_oop())->java_mirror_type(); | |
+ match = ldk2 && mirror_type->is_klass(); | |
+ if (match) { | |
+ match = false; // set to false here so we only match on the exact cases below | |
+ if (ldk2->is_DecodeN()) { | |
+ if (ldk2->in(1)->Opcode() == Op_LoadNKlass) { | |
+ match = true; | |
+ } | |
+ } else if (ldk2->Opcode() == Op_LoadKlass) { | |
+ match = true; | |
+ } | |
+ } | |
+ | |
+ assert(!match || off == in_bytes(Klass::java_mirror_offset()), | |
+ "should be loading mirror from java_mirror_offset"); | |
+ | |
+ if (match) { | |
+ Node* lhs = ldk2; | |
+ Node* rhs = phase->makecon(TypeKlassPtr::make((ciKlass*) mirror_type)); | |
+ 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 Wed Apr 25 22:06:18 2012 +0800 | |
@@ -1171,6 +1171,34 @@ | |
// branch, seeing how it constrains a tested value, and then | |
// deciding if it's worth our while to encode this constraint | |
// as graph nodes in the current abstract interpretation map. | |
+static Node* extract_obj_from_load_klass(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); | |
+ if (!obj) return NULL; | |
+ assert(off == oopDesc::klass_offset_in_bytes(), | |
+ "should be loading klass from obj"); | |
+ | |
+ // obj may be a CastPP, introduced when parsing invokevirtual on getClass() | |
+ // Parse::do_call() -> GraphKit::cast_not_null() | |
+ // TODO should we preserve the cast, or should we uncast here? | |
+ return obj; // preserve the cast (if any) for now | |
+} | |
+ | |
void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, | |
Block* path, Block* other_path) { | |
if (stopped() || !c->is_Cmp() || btest == BoolTest::illegal) | |
@@ -1234,6 +1262,25 @@ | |
return; | |
+ Node* obj = extract_obj_from_load_klass(&_gvn, val); | |
+ const TypeKlassPtr* tkp = tcon->isa_klassptr(); | |
+ if (obj && tkp && btest == BoolTest::eq) { | |
+ // 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. | |
+ const TypeOopPtr* obj_xtype = tkp->as_instance_type(); | |
+ assert(obj_xtype->higher_equal(_gvn.type(obj)), "must improve"); | |
+ | |
+ 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 Wed Apr 25 22:06:18 2012 +0800 | |
@@ -703,11 +703,89 @@ | |
} | |
//------------------------------Ideal------------------------------------------ | |
-// Check for the case of comparing an unknown klass loaded from the primary | |
+// Normalize comparisons between two Java mirror returned from getClass(), or | |
+// comparisons between a Java mirror returned from getClass() vs a Java mirror | |
+// constant. Simplify the pattern 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. | |
+static inline bool is_java_mirror_ptr(PhaseGVN* phase, const TypeOopPtr* ptr) { | |
+ return ptr && ptr->klass_is_exact() && | |
+ ptr->klass() == phase->C->env()->Class_klass(); | |
+} | |
+ | |
+static inline Node* isa_java_getClass(PhaseGVN* phase, Node* n) { | |
+ // return the klass node for | |
+ // LoadP(LoadKlass(obj._klass).java_mirror) | |
+ // or | |
+ // LoadP(DecodeN(LoadNKlass(obj._compressed_klass)).java_mirror) | |
+ // , or NULL if not matching | |
+ if (n->Opcode() != Op_LoadP) return NULL; | |
+ | |
+ const TypeOopPtr* tp = phase->type(n)->isa_oopptr(); | |
+ if (!is_java_mirror_ptr(phase, tp)) return NULL; | |
+ | |
+ Node* adr = n->in(MemNode::Address); | |
+ intptr_t off = 0; | |
+ Node* ldk = AddPNode::Ideal_base_and_offset(adr, phase, off); | |
+ | |
+ if (ldk->is_DecodeN()) { | |
+ if (ldk->in(1)->Opcode() != Op_LoadNKlass) { | |
+ return NULL; | |
+ } | |
+ } else if (ldk->Opcode() != Op_LoadKlass) { | |
+ return NULL; | |
+ } | |
+ | |
+ assert(off == in_bytes(Klass::java_mirror_offset()), | |
+ "should be loading mirror from java_mirror_offset"); | |
+ 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 TypeOopPtr* tp = phase->type(n)->isa_oopptr(); | |
+ if (!is_java_mirror_ptr(phase, tp)) return NULL; | |
+ | |
+ ciType* mirror_type = ((ciInstance*) tp->const_oop())->java_mirror_type(); | |
+ if (!mirror_type->is_klass()) return NULL; | |
+ | |
+ return phase->makecon(TypeKlassPtr::make((ciKlass*) mirror_type)); | |
+} | |
+ | |
Node *CmpPNode::Ideal( PhaseGVN *phase, bool can_reshape ) { | |
+ // Normalize the code pattern | |
+ // x.getClass() == Foo.class into x._klass == Foo.klass | |
+ // or | |
+ // x.getClass() == y.getClass() into x._klass == y._klass | |
+ // 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_getClass(phase, in(1)); | |
+ Node* ldk2 = isa_java_getClass(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()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment