mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 03:24:38 +02:00
7170463: C2 should recognize "obj.getClass() == A.class" code pattern
Optimize this code pattern obj.getClass() == A.class. Reviewed-by: jrose, kvn
This commit is contained in:
parent
d6d1826ad3
commit
a9ea68bd3f
3 changed files with 142 additions and 1 deletions
|
@ -527,6 +527,9 @@ class Parse : public GraphKit {
|
||||||
int repush_if_args();
|
int repush_if_args();
|
||||||
void adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
|
void adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
|
||||||
Block* path, Block* other_path);
|
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);
|
IfNode* jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask);
|
||||||
Node* jump_if_join(Node* iffalse, Node* iftrue);
|
Node* jump_if_join(Node* iffalse, Node* iftrue);
|
||||||
void jump_if_true_fork(IfNode *ifNode, int dest_bci_if_true, int prof_table_index);
|
void jump_if_true_fork(IfNode *ifNode, int dest_bci_if_true, int prof_table_index);
|
||||||
|
|
|
@ -1233,6 +1233,71 @@ void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
|
||||||
if (!have_con) // remaining adjustments need a con
|
if (!have_con) // remaining adjustments need a con
|
||||||
return;
|
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);
|
int val_in_map = map()->find_edge(val);
|
||||||
if (val_in_map < 0) return; // replace_in_map would be useless
|
if (val_in_map < 0) return; // replace_in_map would be useless
|
||||||
|
@ -1265,6 +1330,7 @@ void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
|
||||||
// Exclude tests vs float/double 0 as these could be
|
// Exclude tests vs float/double 0 as these could be
|
||||||
// either +0 or -0. Just because you are equal to +0
|
// either +0 or -0. Just because you are equal to +0
|
||||||
// doesn't mean you ARE +0!
|
// doesn't mean you ARE +0!
|
||||||
|
// Note, following code also replaces Long and Oop values.
|
||||||
if ((!tf || tf->_f != 0.0) &&
|
if ((!tf || tf->_f != 0.0) &&
|
||||||
(!td || td->_d != 0.0))
|
(!td || td->_d != 0.0))
|
||||||
cast = con; // Replace non-constant val by con.
|
cast = con; // Replace non-constant val by con.
|
||||||
|
|
|
@ -702,12 +702,84 @@ const Type *CmpPNode::sub( const Type *t1, const Type *t2 ) const {
|
||||||
return TypeInt::CC;
|
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------------------------------------------
|
//------------------------------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
|
// 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;
|
// 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.
|
// this only happens on an exact match. We can shorten this test by 1 load.
|
||||||
Node *CmpPNode::Ideal( PhaseGVN *phase, bool can_reshape ) {
|
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?
|
// Constant pointer on right?
|
||||||
const TypeKlassPtr* t2 = phase->type(in(2))->isa_klassptr();
|
const TypeKlassPtr* t2 = phase->type(in(2))->isa_klassptr();
|
||||||
if (t2 == NULL || !t2->klass_is_exact())
|
if (t2 == NULL || !t2->klass_is_exact())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue