Skip to content

Instantly share code, notes, and snippets.

@rednaxelafx
Created May 6, 2012 14:44
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/2622617 to your computer and use it in GitHub Desktop.
Save rednaxelafx/2622617 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/2499019
diff -r bfcf92bfefb8 src/share/vm/opto/parse2.cpp
--- a/src/share/vm/opto/parse2.cpp Thu Apr 26 14:05:31 2012 -0700
+++ b/src/share/vm/opto/parse2.cpp Sun May 06 22:42:44 2012 +0800
@@ -1165,6 +1165,138 @@
}
}
+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;
+}
+
+static void sharpen_type_after_if(Parse* p, BoolTest::mask btest,
+ Node* con, const Type* tcon,
+ Node* val, const Type* tval) {
+ Compile* C = p->C;
+ PhaseGVN& gvn = p->gvn();
+
+ // look for opportunities to sharpen the type of a node
+ // whose klass is compared with a constant klass
+ Node* obj = extract_obj_from_klass_load(&gvn, val);
+ const TypeKlassPtr* tkp = tcon->isa_klassptr();
+ bool is_klass_cmp = obj && tkp;
+
+ // the node to be replaced, and its type
+ Node* rep;
+ const Type* trep;
+ if (is_klass_cmp) {
+ const Type* tobj = gvn.type(obj);
+ rep = obj;
+ trep = tobj;
+ } else {
+ rep = val;
+ trep = tval;
+ }
+
+ int rep_in_map = p->map()->find_edge(rep);
+ if (rep_in_map < 0) return; // replace_in_map would be useless
+ {
+ JVMState* jvms = p->jvms();
+ if (!(jvms->is_loc(rep_in_map) ||
+ jvms->is_stk(rep_in_map)))
+ return; // again, it would be useless
+ }
+
+ // Check for a comparison to a constant, and "know" that the compared
+ // value is constrained on this path.
+ assert(tcon->singleton(), "");
+ TypeNode* ccast = NULL;
+ Node* cast = NULL;
+
+ switch (btest) {
+ case BoolTest::eq: // Constant test?
+ {
+ if (is_klass_cmp) {
+ 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.
+ const TypeOopPtr* tboth = obj_xtype->join(trep)->is_oopptr();
+ if (tboth == trep) break; // Nothing to gain.
+
+ assert(tboth->klass_is_exact(), "klass should be exact");
+ ccast = new (C, 2) CheckCastPPNode(p->control(), obj, tboth);
+ }
+ } else {
+ const Type* tboth = tcon->join(tval);
+ if (tboth == tval) break; // Nothing to gain.
+ if (tcon->isa_int()) {
+ ccast = new (C, 2) CastIINode(val, tboth);
+ } else if (tcon == TypePtr::NULL_PTR) {
+ // Cast to null, but keep the pointer identity temporarily live.
+ ccast = new (C, 2) CastPPNode(val, tboth);
+ } else {
+ const TypeF* tf = tcon->isa_float_constant();
+ const TypeD* td = tcon->isa_double_constant();
+ // 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!
+ if ((!tf || tf->_f != 0.0) &&
+ (!td || td->_d != 0.0))
+ cast = con; // Replace non-constant val by con.
+ }
+ }
+ }
+ break;
+
+ case BoolTest::ne:
+ if (tcon == TypePtr::NULL_PTR) {
+ cast = p->cast_not_null(val, false);
+ }
+ break;
+
+ default:
+ // (At this point we could record int range types with CastII.)
+ break;
+ }
+
+ if (ccast != NULL) {
+ const Type* tcc = ccast->as_Type()->type();
+ assert(tcc != trep && tcc->higher_equal(trep), "must improve");
+ // Delay transform() call to allow recovery of pre-cast value
+ // at the control merge.
+ ccast->set_req(0, p->control());
+ gvn.set_type_bottom(ccast);
+ p->record_for_igvn(ccast);
+ cast = ccast;
+ }
+
+ if (cast != NULL) { // Here's the payoff.
+ p->replace_in_map(rep, cast);
+ }
+}
+
//----------------------------adjust_map_after_if------------------------------
// Adjust the JVM state to reflect the result of taking this path.
// Basically, it means inspecting the CmpNode controlling this
@@ -1233,70 +1365,7 @@
if (!have_con) // remaining adjustments need a con
return;
-
- int val_in_map = map()->find_edge(val);
- if (val_in_map < 0) return; // replace_in_map would be useless
- {
- JVMState* jvms = this->jvms();
- if (!(jvms->is_loc(val_in_map) ||
- jvms->is_stk(val_in_map)))
- return; // again, it would be useless
- }
-
- // Check for a comparison to a constant, and "know" that the compared
- // value is constrained on this path.
- assert(tcon->singleton(), "");
- ConstraintCastNode* ccast = NULL;
- Node* cast = NULL;
-
- switch (btest) {
- case BoolTest::eq: // Constant test?
- {
- const Type* tboth = tcon->join(tval);
- if (tboth == tval) break; // Nothing to gain.
- if (tcon->isa_int()) {
- ccast = new (C, 2) CastIINode(val, tboth);
- } else if (tcon == TypePtr::NULL_PTR) {
- // Cast to null, but keep the pointer identity temporarily live.
- ccast = new (C, 2) CastPPNode(val, tboth);
- } else {
- const TypeF* tf = tcon->isa_float_constant();
- const TypeD* td = tcon->isa_double_constant();
- // 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!
- if ((!tf || tf->_f != 0.0) &&
- (!td || td->_d != 0.0))
- cast = con; // Replace non-constant val by con.
- }
- }
- break;
-
- case BoolTest::ne:
- if (tcon == TypePtr::NULL_PTR) {
- cast = cast_not_null(val, false);
- }
- break;
-
- default:
- // (At this point we could record int range types with CastII.)
- break;
- }
-
- if (ccast != NULL) {
- const Type* tcc = ccast->as_Type()->type();
- assert(tcc != tval && tcc->higher_equal(tval), "must improve");
- // Delay transform() call to allow recovery of pre-cast value
- // at the control merge.
- ccast->set_req(0, control());
- _gvn.set_type_bottom(ccast);
- record_for_igvn(ccast);
- cast = ccast;
- }
-
- if (cast != NULL) { // Here's the payoff.
- replace_in_map(val, cast);
- }
+ sharpen_type_after_if(this, btest, con, tcon, val, tval);
}
diff -r bfcf92bfefb8 src/share/vm/opto/subnode.cpp
--- a/src/share/vm/opto/subnode.cpp Thu Apr 26 14:05:31 2012 -0700
+++ b/src/share/vm/opto/subnode.cpp Sun May 06 22:42:44 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())
(gdb) run -XX:-UseTypeProfile -XX:-TieredCompilation InstanceofTest
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/sajia/testjdk7u1/bin/java -XX:-UseTypeProfile -XX:-TieredCompilation InstanceofTest
[Thread debugging using libthread_db enabled]
[New Thread 0x2b1a8f8de610 (LWP 26198)]
[New Thread 0x401d4940 (LWP 26199)]
[New Thread 0x402d5940 (LWP 26200)]
[New Thread 0x40c73940 (LWP 26201)]
[New Thread 0x40d74940 (LWP 26202)]
[New Thread 0x40e75940 (LWP 26203)]
[New Thread 0x40f76940 (LWP 26204)]
[New Thread 0x41077940 (LWP 26205)]
[New Thread 0x41178940 (LWP 26206)]
[New Thread 0x41c5e940 (LWP 26207)]
[New Thread 0x41d5f940 (LWP 26208)]
[New Thread 0x41e60940 (LWP 26209)]
[New Thread 0x41f61940 (LWP 26210)]
[New Thread 0x42062940 (LWP 26211)]
[New Thread 0x42163940 (LWP 26212)]
CompilerOracle: exclude InstanceofTest.main
CompilerOracle: exclude A.<init>
CompilerOracle: exclude A.dump
CompilerOracle: exclude java*.*
CompilerOracle: exclude sun*.*
CompilerOracle: exclude com*.*
[New Thread 0x41279940 (LWP 26213)]
[New Thread 0x42264940 (LWP 26214)]
[New Thread 0x42365940 (LWP 26215)]
[New Thread 0x41a00940 (LWP 26216)]
[New Thread 0x41b01940 (LWP 26217)]
[New Thread 0x42466940 (LWP 26218)]
[New Thread 0x42567940 (LWP 26219)]
### Excluding generation of native wrapper: java.lang.Object::getClass
[Switching to Thread 0x41b01940 (LWP 26217)]
Breakpoint 1, extract_obj_from_klass_load (gvn=0x41aff190, n=0x17e8d78)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1170
1170 if (n->is_DecodeN()) {
(gdb) next
1171 if (n->in(1)->Opcode() != Op_LoadNKlass) {
(gdb)
1174 ldk = n->in(1);
(gdb)
1181 assert(ldk != NULL, "should have found a LoadKlass or LoadNKlass node");
(gdb)
1183 Node* adr = ldk->in(MemNode::Address);
(gdb)
1184 intptr_t off = 0;
(gdb)
1185 Node* obj = AddPNode::Ideal_base_and_offset(adr, gvn, off);
(gdb)
1186 const TypePtr* tp = gvn->type(obj)->is_ptr();
(gdb)
1187 if ( !obj // is adr an AddP node?
(gdb)
1193 return obj;
(gdb)
1194 }
(gdb)
sharpen_type_after_if (parse=0x41afeed0, btest=BoolTest::ne, con=0x17e9250, tcon=0x17f0dc0, val=0x17e8d78, tval=0x17f0a40)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1203
1203 const TypeKlassPtr* tkp = tcon->isa_klassptr();
(gdb)
1204 if (obj && tkp && btest == BoolTest::eq) {
(gdb)
1224 int val_in_map = parse->map()->find_edge(val);
(gdb)
1225 if (val_in_map < 0) return; // replace_in_map would be useless
(gdb) print obj->dump()
36 CastPP === 28 10 [[ 20 39 39 54 ]] #IFoo:NotNull * Interface:IFoo:NotNull * !jvms: InstanceofTest::test @ bci:4
$5 = void
(gdb) print tkp->dump()
precise klass A: 0x00000000017d6f98:Constant:exact *$6 = void
(gdb) print btest
$7 = BoolTest::ne
(gdb) print BoolTest(btest).commute()
A syntax error in expression, near `btest).commute()'.
(gdb) print ((BoolTest)btest).commute()
$8 = BoolTest::ne
(gdb) cont
Continuing.
Breakpoint 1, extract_obj_from_klass_load (gvn=0x41aff190, n=0x17e8d78)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1170
1170 if (n->is_DecodeN()) {
(gdb) bt
#0 extract_obj_from_klass_load (gvn=0x41aff190, n=0x17e8d78)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1170
#1 0x00002b1a902686cd in sharpen_type_after_if (parse=0x41afeed0, btest=BoolTest::eq, con=0x17e9250, tcon=0x17f0dc0,
val=0x17e8d78, tval=0x17f0a40) at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1202
#2 0x00002b1a902692c8 in Parse::adjust_map_after_if (this=0x41afeed0, btest=BoolTest::eq, c=0x17e91e0, prob=0.499962509,
path=0x17cbb40, other_path=0x17cbb88) at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1358
#3 0x00002b1a9026a0ca in Parse::do_if (this=0x41afeed0, btest=BoolTest::ne, c=0x17e91e0)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1164
#4 0x00002b1a9027281a in Parse::do_one_bytecode (this=0x41afeed0)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:2233
#5 0x00002b1a90263200 in Parse::do_one_block (this=0x41afeed0)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse1.cpp:1405
#6 0x00002b1a90263661 in Parse::do_all_blocks (this=0x41afeed0)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse1.cpp:680
#7 0x00002b1a902665a7 in Parse (this=0x41afeed0, caller=0x17d8118, parse_method=0x2aaab00139b8, expected_uses=10020)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse1.cpp:589
#8 0x00002b1a8fd4284d in ParseGenerator::generate (this=0x17cbac8, jvms=0x17d8118)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/callGenerator.cpp:85
#9 0x00002b1a8fdfa18b in Compile (this=0x41afff30, ci_env=0x41b00900, compiler=0x2aaab0000908, target=0x2aaab00139b8,
osr_bci=-1, subsume_loads=true, do_escape_analysis=true)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/compile.cpp:692
#10 0x00002b1a8fd3da3c in C2Compiler::compile_method (this=0x2aaab0000908, env=0x41b00900, target=0x2aaab00139b8,
entry_bci=-1) at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/c2compiler.cpp:130
#11 0x00002b1a8fe01516 in CompileBroker::invoke_compiler_on_method (task=0x2aaab0027ec8)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/compiler/compileBroker.cpp:1760
#12 0x00002b1a8fe03ed4 in CompileBroker::compiler_thread_loop ()
at /home/sajia/temp/jdk8/hotspot/src/share/vm/compiler/compileBroker.cpp:1597
#13 0x00002b1a90380046 in compiler_thread_entry (thread=0x2aaab0001000, __the_thread__=0x2aaab0001000)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/runtime/thread.cpp:3043
#14 0x00002b1a90383b4a in JavaThread::thread_main_inner (this=0x2aaab0001000)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/runtime/thread.cpp:1548
#15 0x00002b1a903861c9 in JavaThread::run (this=0x2aaab0001000)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/runtime/thread.cpp:1528
#16 0x00002b1a9023cc8b in java_start (thread=0x2aaab0001000)
at /home/sajia/temp/jdk8/hotspot/src/os/linux/vm/os_linux.cpp:886
#17 0x000000347f4064a7 in start_thread () from /lib64/libpthread.so.0
#18 0x000000347ecd3c2d in clone () from /lib64/libc.so.6
(gdb) frame 2
#2 0x00002b1a902692c8 in Parse::adjust_map_after_if (this=0x41afeed0, btest=BoolTest::eq, c=0x17e91e0, prob=0.499962509,
path=0x17cbb40, other_path=0x17cbb88) at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1358
1358 sharpen_type_after_if(this, btest, con, tcon, val, tval);
(gdb) print this->bci()
$9 = 7
(gdb) frame 0
#0 extract_obj_from_klass_load (gvn=0x41aff190, n=0x17e8d78)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1170
1170 if (n->is_DecodeN()) {
(gdb) next
1171 if (n->in(1)->Opcode() != Op_LoadNKlass) {
(gdb)
1174 ldk = n->in(1);
(gdb)
1181 assert(ldk != NULL, "should have found a LoadKlass or LoadNKlass node");
(gdb)
1183 Node* adr = ldk->in(MemNode::Address);
(gdb)
1184 intptr_t off = 0;
(gdb)
1185 Node* obj = AddPNode::Ideal_base_and_offset(adr, gvn, off);
(gdb)
1186 const TypePtr* tp = gvn->type(obj)->is_ptr();
(gdb)
1187 if ( !obj // is adr an AddP node?
(gdb)
1193 return obj;
(gdb)
1194 }
(gdb)
sharpen_type_after_if (parse=0x41afeed0, btest=BoolTest::eq, con=0x17e9250, tcon=0x17f0dc0, val=0x17e8d78, tval=0x17f0a40)
at /home/sajia/temp/jdk8/hotspot/src/share/vm/opto/parse2.cpp:1203
1203 const TypeKlassPtr* tkp = tcon->isa_klassptr();
(gdb)
1204 if (obj && tkp && btest == BoolTest::eq) {
(gdb)
1205 const TypeOopPtr* obj_xtype = tkp->as_instance_type();
(gdb)
1206 if (obj_xtype->isa_instptr() || obj_xtype->isa_aryptr()) {
(gdb)
1211 assert(obj_xtype->higher_equal(gvn.type(obj)), "must improve");
(gdb) print obj_xtype->join(gvn.type(obj))
$10 = (const Type *) 0x17f0fb0
(gdb) print $10->_base
$11 = Type::InstPtr
(gdb) print $10->dump()
A:NotNull:exact *$12 = void
(gdb) print obj_xtype->dump()
A:NotNull:exact *$13 = void
(gdb) quit
The program is running. Exit anyway? (y or n) y
$
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment