8267979: C2: Fix verification code in SubTypeCheckNode::Ideal()

Reviewed-by: neliasso, roland
This commit is contained in:
Vladimir Ivanov 2021-06-01 09:15:46 +00:00
parent ae2f37f868
commit f5634fe39d
2 changed files with 109 additions and 101 deletions

View File

@ -103,7 +103,7 @@ const Type* SubTypeCheckNode::sub(const Type* sub_t, const Type* super_t) const
return bottom_type();
}
Node *SubTypeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
Node *SubTypeCheckNode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node* obj_or_subklass = in(ObjOrSubKlass);
Node* superklass = in(SuperKlass);
@ -134,14 +134,7 @@ Node *SubTypeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
intptr_t con = 0;
Node* obj = AddPNode::Ideal_base_and_offset(addr, phase, con);
if (con == oopDesc::klass_offset_in_bytes() && obj != NULL) {
#ifdef ASSERT
const Type* obj_t = phase->type(obj);
if (!obj_t->isa_oopptr() && obj_t != Type::TOP) {
obj->dump();
obj_t->dump(); tty->cr();
fatal("only for oop input");
}
#endif
assert(is_oop(phase, obj), "only for oop input");
set_req(ObjOrSubKlass, obj);
return this;
}
@ -150,104 +143,111 @@ Node *SubTypeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
// AllocateNode might have more accurate klass input
Node* allocated_klass = AllocateNode::Ideal_klass(obj_or_subklass, phase);
if (allocated_klass != NULL) {
#ifdef ASSERT
const Type* obj_or_subklass_t = phase->type(obj_or_subklass);
if (!obj_or_subklass_t->isa_oopptr() && obj_or_subklass_t != Type::TOP) {
obj_or_subklass->dump();
obj_or_subklass_t->dump(); tty->cr();
fatal("only for oop input");
}
#endif
assert(is_oop(phase, obj_or_subklass), "only for oop input");
set_req(ObjOrSubKlass, allocated_klass);
return this;
}
// Verify that optimizing the subtype check to a simple code pattern
// when possible would not constant fold better
#ifdef ASSERT
ciKlass* superk = super_t->is_klassptr()->klass();
ciKlass* subk = sub_t->isa_klassptr() ? sub_t->is_klassptr()->klass() : sub_t->is_oopptr()->klass();
if (super_t->singleton() && subk != NULL && phase->C->static_subtype_check(superk, subk) == Compile::SSC_easy_test) {
Node* subklass = NULL;
if (sub_t->isa_oopptr()) {
Node* adr = phase->transform(new AddPNode(obj_or_subklass, obj_or_subklass, phase->MakeConX(oopDesc::klass_offset_in_bytes())));
subklass = phase->transform(LoadKlassNode::make(*phase, NULL, phase->C->immutable_memory(), adr, TypeInstPtr::KLASS));
} else {
subklass = obj_or_subklass;
}
Node* res = new CmpPNode(subklass, superklass);
Node* cmp = phase->transform(res);
const Type* t = phase->type(cmp);
if (!((Value(phase) == t) || (t != TypeInt::CC_GT && t != TypeInt::CC_EQ))) {
Value(phase)->dump(); tty->cr();
t->dump(); tty->cr();
obj_or_subklass->dump();
subklass->dump();
superklass->dump();
cmp->dump();
tty->print_cr("==============================");
phase->C->root()->dump(9999);
fatal("missing Value() optimization");
}
if (phase->is_IterGVN()) {
phase->is_IterGVN()->_worklist.push(res);
}
return NULL;
}
Node *p1 = phase->transform(new AddPNode(superklass, superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset()))));
Node* m = phase->C->immutable_memory();
LoadINode* chk_off_ld = new LoadINode(NULL, m, p1, phase->type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered);
Node *chk_off = phase->transform(chk_off_ld);
int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset());
bool might_be_cache = (phase->find_int_con(chk_off, cacheoff_con) == cacheoff_con);
if (might_be_cache) {
if (phase->is_IterGVN()) {
phase->is_IterGVN()->_worklist.push(chk_off_ld);
}
return NULL;
}
if (super_t->singleton() && subk != NULL && phase->C->static_subtype_check(superk, subk) == Compile::SSC_full_test) {
Node* subklass = NULL;
if (sub_t->isa_oopptr()) {
Node* adr = phase->transform(new AddPNode(obj_or_subklass, obj_or_subklass, phase->MakeConX(oopDesc::klass_offset_in_bytes())));
subklass = phase->transform(LoadKlassNode::make(*phase, NULL, phase->C->immutable_memory(), adr, TypeInstPtr::KLASS));
} else {
subklass = obj_or_subklass;
}
Node *chk_off_X = chk_off;
#ifdef _LP64
chk_off_X = phase->transform(new ConvI2LNode(chk_off_X));
#endif
Node *p2 = phase->transform(new AddPNode(subklass,subklass,chk_off_X));
Node *kmem = phase->C->immutable_memory();
Node *nkls = phase->transform(LoadKlassNode::make(*phase, NULL, kmem, p2, phase->type(p2)->is_ptr(), TypeKlassPtr::OBJECT_OR_NULL));
Node* res = new CmpPNode(superklass, nkls);
Node* cmp = phase->transform(res);
const Type* t = phase->type(cmp);
if (!((Value(phase) == t) || (t != TypeInt::CC_GT && t != TypeInt::CC_EQ))) {
Value(phase)->dump(); tty->cr();
t->dump(); tty->cr();
obj_or_subklass->dump();
subklass->dump();
superklass->dump();
nkls->dump();
cmp->dump();
tty->print_cr("==============================");
phase->C->root()->dump(9999);
fatal("missing Value() optimization");
}
if (phase->is_IterGVN()) {
phase->is_IterGVN()->_worklist.push(res);
}
return NULL;
}
#endif
assert(verify(phase), "missing Value() optimization");
return NULL;
}
#ifdef ASSERT
bool SubTypeCheckNode::is_oop(PhaseGVN* phase, Node* n) {
const Type* t = phase->type(n);
if (!t->isa_oopptr() && t != Type::TOP) {
n->dump();
t->dump(); tty->cr();
return false;
}
return true;
}
static Node* record_for_cleanup(Node* n, PhaseGVN* phase) {
if (phase->is_IterGVN()) {
phase->is_IterGVN()->_worklist.push(n); // record for cleanup
}
return n;
}
bool SubTypeCheckNode::verify_helper(PhaseGVN* phase, Node* subklass, const Type* cached_t) {
Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass)));
record_for_cleanup(cmp, phase);
const Type* cmp_t = phase->type(cmp);
const Type* t = Value(phase);
if (t == cmp_t ||
t != cached_t || // previous observations don't hold anymore
(cmp_t != TypeInt::CC_GT && cmp_t != TypeInt::CC_EQ)) {
return true;
} else {
t->dump(); tty->cr();
this->dump(2); tty->cr();
cmp_t->dump(); tty->cr();
subklass->dump(2); tty->cr();
tty->print_cr("==============================");
phase->C->root()->dump(9999);
return false;
}
}
// Verify that optimizing the subtype check to a simple code pattern when possible would not constant fold better.
bool SubTypeCheckNode::verify(PhaseGVN* phase) {
Compile* C = phase->C;
Node* obj_or_subklass = in(ObjOrSubKlass);
Node* superklass = in(SuperKlass);
const Type* sub_t = phase->type(obj_or_subklass);
const Type* super_t = phase->type(superklass);
ciKlass* subk = sub_t->isa_klassptr() ? sub_t->is_klassptr()->klass() : sub_t->is_oopptr()->klass(); // can be NULL for bottom[]
ciKlass* superk = super_t->is_klassptr()->klass();
if (super_t->singleton() && subk != NULL) {
Node* subklass = NULL;
if (sub_t->isa_oopptr()) {
Node* adr = phase->transform(new AddPNode(obj_or_subklass, obj_or_subklass, phase->MakeConX(oopDesc::klass_offset_in_bytes())));
subklass = phase->transform(LoadKlassNode::make(*phase, NULL, C->immutable_memory(), adr, TypeInstPtr::KLASS));
record_for_cleanup(subklass, phase);
} else {
subklass = obj_or_subklass;
}
const Type* cached_t = Value(phase); // cache the type to validate consistency
switch (C->static_subtype_check(superk, subk)) {
case Compile::SSC_easy_test: {
return verify_helper(phase, subklass, cached_t);
}
case Compile::SSC_full_test: {
Node* p1 = phase->transform(new AddPNode(superklass, superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset()))));
Node* chk_off = phase->transform(new LoadINode(NULL, C->immutable_memory(), p1, phase->type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered));
record_for_cleanup(chk_off, phase);
int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset());
bool might_be_cache = (phase->find_int_con(chk_off, cacheoff_con) == cacheoff_con);
if (!might_be_cache) {
Node* chk_off_X = chk_off;
#ifdef _LP64
chk_off_X = phase->transform(new ConvI2LNode(chk_off_X));
#endif
Node* p2 = phase->transform(new AddPNode(subklass, subklass, chk_off_X));
Node* nkls = phase->transform(LoadKlassNode::make(*phase, NULL, C->immutable_memory(), p2, phase->type(p2)->is_ptr(), TypeKlassPtr::OBJECT_OR_NULL));
return verify_helper(phase, nkls, cached_t);
}
break;
}
case Compile::SSC_always_false:
case Compile::SSC_always_true:
default: {
break; // nothing to do
}
}
}
return true;
}
#endif

View File

@ -49,6 +49,14 @@ public:
virtual int Opcode() const;
const Type* bottom_type() const { return TypeInt::CC; }
bool depends_only_on_test() const { return false; };
#ifdef ASSERT
private:
bool verify(PhaseGVN* phase);
bool verify_helper(PhaseGVN* phase, Node* subklass, const Type* cached_t);
static bool is_oop(PhaseGVN* phase, Node* n);
#endif // ASSERT
};
#endif // SHARE_OPTO_SUBTYPENODE_HPP