Skip to content

Instantly share code, notes, and snippets.

@rednaxelafx
Created April 24, 2012 16:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rednaxelafx/2481025 to your computer and use it in GitHub Desktop.
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
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())
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