8303737: C2: Load can bypass subtype check that enforces it's from the right object type
Reviewed-by: chagedorn, thartmann
This commit is contained in:
parent
9e6cb62048
commit
52983ed529
@ -37,14 +37,14 @@
|
||||
//=============================================================================
|
||||
// If input is already higher or equal to cast type, then this is an identity.
|
||||
Node* ConstraintCastNode::Identity(PhaseGVN* phase) {
|
||||
if (_dependency == UnconditionalDependency) {
|
||||
return this;
|
||||
}
|
||||
Node* dom = dominating_cast(phase, phase);
|
||||
if (dom != nullptr) {
|
||||
return dom;
|
||||
}
|
||||
if (_dependency != RegularDependency) {
|
||||
return this;
|
||||
}
|
||||
return phase->type(in(1))->higher_equal_speculative(_type) ? in(1) : this;
|
||||
return higher_equal_types(phase, in(1)) ? in(1) : this;
|
||||
}
|
||||
|
||||
//------------------------------Value------------------------------------------
|
||||
@ -101,47 +101,62 @@ Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
return (in(0) && remove_dead_region(phase, can_reshape)) ? this : nullptr;
|
||||
}
|
||||
|
||||
uint ConstraintCastNode::hash() const {
|
||||
return TypeNode::hash() + (int)_dependency + (_extra_types != nullptr ? _extra_types->hash() : 0);
|
||||
}
|
||||
|
||||
bool ConstraintCastNode::cmp(const Node &n) const {
|
||||
return TypeNode::cmp(n) && ((ConstraintCastNode&)n)._dependency == _dependency;
|
||||
if (!TypeNode::cmp(n)) {
|
||||
return false;
|
||||
}
|
||||
ConstraintCastNode& cast = (ConstraintCastNode&) n;
|
||||
if (cast._dependency != _dependency) {
|
||||
return false;
|
||||
}
|
||||
if (_extra_types == nullptr || cast._extra_types == nullptr) {
|
||||
return _extra_types == cast._extra_types;
|
||||
}
|
||||
return _extra_types->eq(cast._extra_types);
|
||||
}
|
||||
|
||||
uint ConstraintCastNode::size_of() const {
|
||||
return sizeof(*this);
|
||||
}
|
||||
|
||||
Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node *n, const Type *t, DependencyType dependency) {
|
||||
Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node* n, const Type* t, DependencyType dependency,
|
||||
const TypeTuple* extra_types) {
|
||||
switch(opcode) {
|
||||
case Op_CastII: {
|
||||
Node* cast = new CastIINode(n, t, dependency);
|
||||
Node* cast = new CastIINode(n, t, dependency, false, extra_types);
|
||||
cast->set_req(0, c);
|
||||
return cast;
|
||||
}
|
||||
case Op_CastLL: {
|
||||
Node* cast = new CastLLNode(n, t, dependency);
|
||||
Node* cast = new CastLLNode(n, t, dependency, extra_types);
|
||||
cast->set_req(0, c);
|
||||
return cast;
|
||||
}
|
||||
case Op_CastPP: {
|
||||
Node* cast = new CastPPNode(n, t, dependency);
|
||||
Node* cast = new CastPPNode(n, t, dependency, extra_types);
|
||||
cast->set_req(0, c);
|
||||
return cast;
|
||||
}
|
||||
case Op_CastFF: {
|
||||
Node* cast = new CastFFNode(n, t, dependency);
|
||||
Node* cast = new CastFFNode(n, t, dependency, extra_types);
|
||||
cast->set_req(0, c);
|
||||
return cast;
|
||||
}
|
||||
case Op_CastDD: {
|
||||
Node* cast = new CastDDNode(n, t, dependency);
|
||||
Node* cast = new CastDDNode(n, t, dependency, extra_types);
|
||||
cast->set_req(0, c);
|
||||
return cast;
|
||||
}
|
||||
case Op_CastVV: {
|
||||
Node* cast = new CastVVNode(n, t, dependency);
|
||||
Node* cast = new CastVVNode(n, t, dependency, extra_types);
|
||||
cast->set_req(0, c);
|
||||
return cast;
|
||||
}
|
||||
case Op_CheckCastPP: return new CheckCastPPNode(c, n, t, dependency);
|
||||
case Op_CheckCastPP: return new CheckCastPPNode(c, n, t, dependency, extra_types);
|
||||
default:
|
||||
fatal("Bad opcode %d", opcode);
|
||||
}
|
||||
@ -151,10 +166,10 @@ Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node *n, const Type *t,
|
||||
Node* ConstraintCastNode::make(Node* c, Node *n, const Type *t, DependencyType dependency, BasicType bt) {
|
||||
switch(bt) {
|
||||
case T_INT: {
|
||||
return make_cast(Op_CastII, c, n, t, dependency);
|
||||
return make_cast(Op_CastII, c, n, t, dependency, nullptr);
|
||||
}
|
||||
case T_LONG: {
|
||||
return make_cast(Op_CastLL, c, n, t, dependency);
|
||||
return make_cast(Op_CastLL, c, n, t, dependency, nullptr);
|
||||
}
|
||||
default:
|
||||
fatal("Bad basic type %s", type2name(bt));
|
||||
@ -187,7 +202,7 @@ TypeNode* ConstraintCastNode::dominating_cast(PhaseGVN* gvn, PhaseTransform* pt)
|
||||
u->outcnt() > 0 &&
|
||||
u->Opcode() == opc &&
|
||||
u->in(0) != nullptr &&
|
||||
u->bottom_type()->higher_equal(type())) {
|
||||
higher_equal_types(gvn, u)) {
|
||||
if (pt->is_dominator(u->in(0), ctl)) {
|
||||
return u->as_Type();
|
||||
}
|
||||
@ -203,9 +218,28 @@ TypeNode* ConstraintCastNode::dominating_cast(PhaseGVN* gvn, PhaseTransform* pt)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ConstraintCastNode::higher_equal_types(PhaseGVN* phase, const Node* other) const {
|
||||
const Type* t = phase->type(other);
|
||||
if (!t->higher_equal_speculative(type())) {
|
||||
return false;
|
||||
}
|
||||
if (_extra_types != nullptr) {
|
||||
for (uint i = 0; i < _extra_types->cnt(); ++i) {
|
||||
if (!t->higher_equal_speculative(_extra_types->field_at(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void ConstraintCastNode::dump_spec(outputStream *st) const {
|
||||
TypeNode::dump_spec(st);
|
||||
if (_extra_types != nullptr) {
|
||||
st->print(" extra types: ");
|
||||
_extra_types->dump_on(st);
|
||||
}
|
||||
if (_dependency != RegularDependency) {
|
||||
st->print(" %s dependency", _dependency == StrongDependency ? "strong" : "unconditional");
|
||||
}
|
||||
@ -524,20 +558,21 @@ Node* CastP2XNode::Identity(PhaseGVN* phase) {
|
||||
return this;
|
||||
}
|
||||
|
||||
Node* ConstraintCastNode::make_cast_for_type(Node* c, Node* in, const Type* type, DependencyType dependency) {
|
||||
Node* ConstraintCastNode::make_cast_for_type(Node* c, Node* in, const Type* type, DependencyType dependency,
|
||||
const TypeTuple* types) {
|
||||
Node* cast= nullptr;
|
||||
if (type->isa_int()) {
|
||||
cast = make_cast(Op_CastII, c, in, type, dependency);
|
||||
cast = make_cast(Op_CastII, c, in, type, dependency, types);
|
||||
} else if (type->isa_long()) {
|
||||
cast = make_cast(Op_CastLL, c, in, type, dependency);
|
||||
cast = make_cast(Op_CastLL, c, in, type, dependency, types);
|
||||
} else if (type->isa_float()) {
|
||||
cast = make_cast(Op_CastFF, c, in, type, dependency);
|
||||
cast = make_cast(Op_CastFF, c, in, type, dependency, types);
|
||||
} else if (type->isa_double()) {
|
||||
cast = make_cast(Op_CastDD, c, in, type, dependency);
|
||||
cast = make_cast(Op_CastDD, c, in, type, dependency, types);
|
||||
} else if (type->isa_vect()) {
|
||||
cast = make_cast(Op_CastVV, c, in, type, dependency);
|
||||
cast = make_cast(Op_CastVV, c, in, type, dependency, types);
|
||||
} else if (type->isa_ptr()) {
|
||||
cast = make_cast(Op_CastPP, c, in, type, dependency);
|
||||
cast = make_cast(Op_CastPP, c, in, type, dependency, types);
|
||||
}
|
||||
return cast;
|
||||
}
|
||||
|
@ -43,11 +43,20 @@ public:
|
||||
const DependencyType _dependency;
|
||||
virtual bool cmp( const Node &n ) const;
|
||||
virtual uint size_of() const;
|
||||
virtual uint hash() const; // Check the type
|
||||
const Type* widen_type(const PhaseGVN* phase, const Type* res, BasicType bt) const;
|
||||
|
||||
private:
|
||||
// PhiNode::Ideal() transforms a Phi that merges a single uncasted value into a single cast pinned at the region.
|
||||
// The types of cast nodes eliminated as a consequence of this transformation are collected and stored here so the
|
||||
// type dependencies carried by the cast are known. The cast can then be eliminated if the type of its input is
|
||||
// narrower (or equal) than all the types it carries.
|
||||
const TypeTuple* _extra_types;
|
||||
|
||||
public:
|
||||
ConstraintCastNode(Node *n, const Type *t, DependencyType dependency)
|
||||
: TypeNode(t,2), _dependency(dependency) {
|
||||
ConstraintCastNode(Node* n, const Type* t, ConstraintCastNode::DependencyType dependency,
|
||||
const TypeTuple* extra_types)
|
||||
: TypeNode(t,2), _dependency(dependency), _extra_types(extra_types) {
|
||||
init_class_id(Class_ConstraintCast);
|
||||
init_req(1, n);
|
||||
}
|
||||
@ -59,14 +68,15 @@ public:
|
||||
virtual bool depends_only_on_test() const { return _dependency == RegularDependency; }
|
||||
bool carry_dependency() const { return _dependency != RegularDependency; }
|
||||
TypeNode* dominating_cast(PhaseGVN* gvn, PhaseTransform* pt) const;
|
||||
static Node* make_cast(int opcode, Node* c, Node *n, const Type *t, DependencyType dependency);
|
||||
static Node* make_cast(int opcode, Node* c, Node* n, const Type* t, DependencyType dependency, const TypeTuple* extra_types);
|
||||
static Node* make(Node* c, Node *n, const Type *t, DependencyType dependency, BasicType bt);
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
#endif
|
||||
|
||||
static Node* make_cast_for_type(Node* c, Node* in, const Type* type, DependencyType dependency);
|
||||
static Node* make_cast_for_type(Node* c, Node* in, const Type* type, DependencyType dependency,
|
||||
const TypeTuple* types);
|
||||
|
||||
Node* optimize_integer_cast(PhaseGVN* phase, BasicType bt);
|
||||
|
||||
@ -91,6 +101,16 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool higher_equal_types(PhaseGVN* phase, const Node* other) const;
|
||||
|
||||
int extra_types_count() const {
|
||||
return _extra_types == nullptr ? 0 : _extra_types->cnt();
|
||||
}
|
||||
|
||||
const Type* extra_type_at(int i) const {
|
||||
return _extra_types->field_at(i);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------CastIINode-------------------------------------
|
||||
@ -103,12 +123,12 @@ class CastIINode: public ConstraintCastNode {
|
||||
virtual uint size_of() const;
|
||||
|
||||
public:
|
||||
CastIINode(Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false)
|
||||
: ConstraintCastNode(n, t, dependency), _range_check_dependency(range_check_dependency) {
|
||||
CastIINode(Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false, const TypeTuple* types = nullptr)
|
||||
: ConstraintCastNode(n, t, dependency, types), _range_check_dependency(range_check_dependency) {
|
||||
init_class_id(Class_CastII);
|
||||
}
|
||||
CastIINode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false)
|
||||
: ConstraintCastNode(n, t, dependency), _range_check_dependency(range_check_dependency) {
|
||||
: ConstraintCastNode(n, t, dependency, nullptr), _range_check_dependency(range_check_dependency) {
|
||||
init_class_id(Class_CastII);
|
||||
init_req(0, ctrl);
|
||||
}
|
||||
@ -134,12 +154,12 @@ class CastIINode: public ConstraintCastNode {
|
||||
class CastLLNode: public ConstraintCastNode {
|
||||
public:
|
||||
CastLLNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
||||
: ConstraintCastNode(n, t, dependency) {
|
||||
: ConstraintCastNode(n, t, dependency, nullptr) {
|
||||
init_class_id(Class_CastLL);
|
||||
init_req(0, ctrl);
|
||||
}
|
||||
CastLLNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
||||
: ConstraintCastNode(n, t, dependency){
|
||||
CastLLNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||
: ConstraintCastNode(n, t, dependency, types) {
|
||||
init_class_id(Class_CastLL);
|
||||
}
|
||||
|
||||
@ -151,8 +171,8 @@ public:
|
||||
|
||||
class CastFFNode: public ConstraintCastNode {
|
||||
public:
|
||||
CastFFNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
||||
: ConstraintCastNode(n, t, dependency){
|
||||
CastFFNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||
: ConstraintCastNode(n, t, dependency, types) {
|
||||
init_class_id(Class_CastFF);
|
||||
}
|
||||
virtual int Opcode() const;
|
||||
@ -161,8 +181,8 @@ public:
|
||||
|
||||
class CastDDNode: public ConstraintCastNode {
|
||||
public:
|
||||
CastDDNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
||||
: ConstraintCastNode(n, t, dependency){
|
||||
CastDDNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||
: ConstraintCastNode(n, t, dependency, types) {
|
||||
init_class_id(Class_CastDD);
|
||||
}
|
||||
virtual int Opcode() const;
|
||||
@ -171,8 +191,8 @@ public:
|
||||
|
||||
class CastVVNode: public ConstraintCastNode {
|
||||
public:
|
||||
CastVVNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
||||
: ConstraintCastNode(n, t, dependency){
|
||||
CastVVNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||
: ConstraintCastNode(n, t, dependency, types) {
|
||||
init_class_id(Class_CastVV);
|
||||
}
|
||||
virtual int Opcode() const;
|
||||
@ -184,8 +204,8 @@ public:
|
||||
// cast pointer to pointer (different type)
|
||||
class CastPPNode: public ConstraintCastNode {
|
||||
public:
|
||||
CastPPNode (Node *n, const Type *t, DependencyType dependency = RegularDependency)
|
||||
: ConstraintCastNode(n, t, dependency) {
|
||||
CastPPNode (Node *n, const Type *t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||
: ConstraintCastNode(n, t, dependency, types) {
|
||||
}
|
||||
virtual int Opcode() const;
|
||||
virtual uint ideal_reg() const { return Op_RegP; }
|
||||
@ -195,8 +215,8 @@ class CastPPNode: public ConstraintCastNode {
|
||||
// for _checkcast, cast pointer to pointer (different type), without JOIN,
|
||||
class CheckCastPPNode: public ConstraintCastNode {
|
||||
public:
|
||||
CheckCastPPNode(Node *c, Node *n, const Type *t, DependencyType dependency = RegularDependency)
|
||||
: ConstraintCastNode(n, t, dependency) {
|
||||
CheckCastPPNode(Node *c, Node *n, const Type *t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||
: ConstraintCastNode(n, t, dependency, types) {
|
||||
init_class_id(Class_CheckCastPP);
|
||||
init_req(0, c);
|
||||
}
|
||||
|
@ -2133,10 +2133,12 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Add casts to carry the control dependency of the Phi that is
|
||||
// going away
|
||||
Node* cast = nullptr;
|
||||
const TypeTuple* extra_types = collect_types(phase);
|
||||
if (phi_type->isa_ptr()) {
|
||||
const Type* uin_type = phase->type(uin);
|
||||
if (!phi_type->isa_oopptr() && !uin_type->isa_oopptr()) {
|
||||
cast = ConstraintCastNode::make_cast(Op_CastPP, r, uin, phi_type, ConstraintCastNode::StrongDependency);
|
||||
cast = ConstraintCastNode::make_cast(Op_CastPP, r, uin, phi_type, ConstraintCastNode::StrongDependency,
|
||||
extra_types);
|
||||
} else {
|
||||
// Use a CastPP for a cast to not null and a CheckCastPP for
|
||||
// a cast to a new klass (and both if both null-ness and
|
||||
@ -2146,7 +2148,8 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// null, uin's type must be casted to not null
|
||||
if (phi_type->join(TypePtr::NOTNULL) == phi_type->remove_speculative() &&
|
||||
uin_type->join(TypePtr::NOTNULL) != uin_type->remove_speculative()) {
|
||||
cast = ConstraintCastNode::make_cast(Op_CastPP, r, uin, TypePtr::NOTNULL, ConstraintCastNode::StrongDependency);
|
||||
cast = ConstraintCastNode::make_cast(Op_CastPP, r, uin, TypePtr::NOTNULL,
|
||||
ConstraintCastNode::StrongDependency, extra_types);
|
||||
}
|
||||
|
||||
// If the type of phi and uin, both casted to not null,
|
||||
@ -2158,14 +2161,16 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
cast = phase->transform(cast);
|
||||
n = cast;
|
||||
}
|
||||
cast = ConstraintCastNode::make_cast(Op_CheckCastPP, r, n, phi_type, ConstraintCastNode::StrongDependency);
|
||||
cast = ConstraintCastNode::make_cast(Op_CheckCastPP, r, n, phi_type, ConstraintCastNode::StrongDependency,
|
||||
extra_types);
|
||||
}
|
||||
if (cast == nullptr) {
|
||||
cast = ConstraintCastNode::make_cast(Op_CastPP, r, uin, phi_type, ConstraintCastNode::StrongDependency);
|
||||
cast = ConstraintCastNode::make_cast(Op_CastPP, r, uin, phi_type, ConstraintCastNode::StrongDependency,
|
||||
extra_types);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cast = ConstraintCastNode::make_cast_for_type(r, uin, phi_type, ConstraintCastNode::StrongDependency);
|
||||
cast = ConstraintCastNode::make_cast_for_type(r, uin, phi_type, ConstraintCastNode::StrongDependency, extra_types);
|
||||
}
|
||||
assert(cast != nullptr, "cast should be set");
|
||||
cast = phase->transform(cast);
|
||||
@ -2560,6 +2565,52 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
return progress; // Return any progress
|
||||
}
|
||||
|
||||
static int compare_types(const Type* const& e1, const Type* const& e2) {
|
||||
return (intptr_t)e1 - (intptr_t)e2;
|
||||
}
|
||||
|
||||
// Collect types at casts that are going to be eliminated at that Phi and store them in a TypeTuple.
|
||||
// Sort the types using an arbitrary order so a list of some types always hashes to the same TypeTuple (and TypeTuple
|
||||
// pointer comparison is enough to tell if 2 list of types are the same or not)
|
||||
const TypeTuple* PhiNode::collect_types(PhaseGVN* phase) const {
|
||||
const Node* region = in(0);
|
||||
const Type* phi_type = bottom_type();
|
||||
ResourceMark rm;
|
||||
GrowableArray<const Type*> types;
|
||||
for (uint i = 1; i < req(); i++) {
|
||||
if (region->in(i) == nullptr || phase->type(region->in(i)) == Type::TOP) {
|
||||
continue;
|
||||
}
|
||||
Node* in = Node::in(i);
|
||||
const Type* t = phase->type(in);
|
||||
if (in == nullptr || in == this || t == Type::TOP) {
|
||||
continue;
|
||||
}
|
||||
if (t != phi_type && t->higher_equal_speculative(phi_type)) {
|
||||
types.insert_sorted<compare_types>(t);
|
||||
}
|
||||
while (in != nullptr && in->is_ConstraintCast()) {
|
||||
Node* next = in->in(1);
|
||||
if (phase->type(next)->isa_rawptr() && phase->type(in)->isa_oopptr()) {
|
||||
break;
|
||||
}
|
||||
ConstraintCastNode* cast = in->as_ConstraintCast();
|
||||
for (int j = 0; j < cast->extra_types_count(); ++j) {
|
||||
const Type* extra_t = cast->extra_type_at(j);
|
||||
if (extra_t != phi_type && extra_t->higher_equal_speculative(phi_type)) {
|
||||
types.insert_sorted<compare_types>(extra_t);
|
||||
}
|
||||
}
|
||||
in = next;
|
||||
}
|
||||
}
|
||||
const Type **flds = (const Type **)(phase->C->type_arena()->AmallocWords(types.length()*sizeof(Type*)));
|
||||
for (int i = 0; i < types.length(); ++i) {
|
||||
flds[i] = types.at(i);
|
||||
}
|
||||
return TypeTuple::make(types.length(), flds);
|
||||
}
|
||||
|
||||
Node* PhiNode::clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn) {
|
||||
Node_Stack stack(1);
|
||||
VectorSet visited;
|
||||
|
@ -268,6 +268,8 @@ public:
|
||||
#else //ASSERT
|
||||
void verify_adr_type(bool recursive = false) const {}
|
||||
#endif //ASSERT
|
||||
|
||||
const TypeTuple* collect_types(PhaseGVN* phase) const;
|
||||
};
|
||||
|
||||
//------------------------------GotoNode---------------------------------------
|
||||
|
@ -1728,7 +1728,8 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
|
||||
Node* in = x->in(k);
|
||||
if (in != nullptr && n_loop->is_member(get_loop(get_ctrl(in)))) {
|
||||
const Type* in_t = _igvn.type(in);
|
||||
cast = ConstraintCastNode::make_cast_for_type(x_ctrl, in, in_t, ConstraintCastNode::UnconditionalDependency);
|
||||
cast = ConstraintCastNode::make_cast_for_type(x_ctrl, in, in_t,
|
||||
ConstraintCastNode::UnconditionalDependency, nullptr);
|
||||
}
|
||||
if (cast != nullptr) {
|
||||
Node* prev = _igvn.hash_find_insert(cast);
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8303737
|
||||
* @summary C2: cast nodes from PhiNode::Ideal() cause "Base pointers must match" assert failure
|
||||
* @requires vm.gc.Parallel
|
||||
* @requires vm.compiler2.enabled
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:LoopMaxUnroll=2 -XX:+UseParallelGC -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
|
||||
* -XX:-UseLoopPredicate -XX:-UseProfiledLoopPredicate -XX:StressSeed=2953783466 TestAddPChainMismatchedBase
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:LoopMaxUnroll=2 -XX:+UseParallelGC -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
|
||||
* -XX:-UseLoopPredicate -XX:-UseProfiledLoopPredicate TestAddPChainMismatchedBase
|
||||
*/
|
||||
|
||||
public class TestAddPChainMismatchedBase {
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
test();
|
||||
testHelper(null, true);
|
||||
testHelper2(1000);
|
||||
}
|
||||
}
|
||||
|
||||
private static void test() {
|
||||
int l;
|
||||
for (l = 0; l < 5; l++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
}
|
||||
}
|
||||
testHelper2(l);
|
||||
}
|
||||
|
||||
private static void testHelper2(int l) {
|
||||
int[] array = new int[1000];
|
||||
if (l == 5) {
|
||||
l = 4;
|
||||
} else {
|
||||
l = 1000;
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
int v = 0;
|
||||
int i = 0;
|
||||
for (; ; ) {
|
||||
synchronized (new Object()) {
|
||||
}
|
||||
array = testHelper(array, false);
|
||||
v += array[i];
|
||||
int j = i;
|
||||
i++;
|
||||
if (i >= l) {
|
||||
break;
|
||||
}
|
||||
array[j] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] testHelper(int[] array, boolean flag) {
|
||||
if (flag) {
|
||||
return new int[1000];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8303737
|
||||
* @summary C2: cast nodes from PhiNode::Ideal() cause "Base pointers must match" assert failure
|
||||
* @run main/othervm -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN -XX:+StressCCP -Xcomp
|
||||
* -XX:CompileOnly=TestAddPChainMismatchedBase2::* -XX:StressSeed=1581936900 TestAddPChainMismatchedBase2
|
||||
* @run main/othervm -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN -XX:+StressCCP -Xcomp
|
||||
* -XX:CompileOnly=TestAddPChainMismatchedBase2::* TestAddPChainMismatchedBase2
|
||||
*/
|
||||
|
||||
public class TestAddPChainMismatchedBase2 {
|
||||
static final int N = 400;
|
||||
static int iFld;
|
||||
|
||||
public static void main(String[] strArr) {
|
||||
test(8);
|
||||
}
|
||||
|
||||
static void test(int i2) {
|
||||
int i12 = 4, iArr1[] = new int[N];
|
||||
double d1, dArr2[] = new double[N];
|
||||
do {
|
||||
iArr1[i12] = 400907;
|
||||
try {
|
||||
iArr1[1] = 47 % i2;
|
||||
} catch (ArithmeticException a_e) {
|
||||
}
|
||||
iArr1[i12 + 1] -= d1 = 1;
|
||||
while ((d1 += 2) < 5) {
|
||||
iArr1 = iArr1;
|
||||
iArr1[6] = 3;
|
||||
}
|
||||
} while (++i12 < 14);
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
/*
|
||||
* @test
|
||||
* @bug 8303737
|
||||
* @summary C2: Load can bypass subtype check that enforces it's from the right object type
|
||||
* @requires vm.gc.Parallel
|
||||
* @requires vm.compiler2.enabled
|
||||
* @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileOnly=TestLoadBypassesClassCast::test
|
||||
* -XX:CompileThreshold=20000 -XX:LoopMaxUnroll=1 -XX:-LoopUnswitching -XX:+UseParallelGC TestLoadBypassesClassCast
|
||||
*
|
||||
*/
|
||||
|
||||
public class TestLoadBypassesClassCast {
|
||||
private static Object saved_o;
|
||||
private static Object field_o = new A();
|
||||
private static Object saved_casted_o;
|
||||
private static float barrier;
|
||||
private static Object[] memory = new Object[100];
|
||||
|
||||
public static void main(String[] args) {
|
||||
float[] array = new float[100];
|
||||
A a = new A();
|
||||
B b = new B();
|
||||
C c = new C();
|
||||
D d = new D();
|
||||
|
||||
// create garbage so GC runs
|
||||
Thread thread = new Thread() {
|
||||
public void run() {
|
||||
while (true) {
|
||||
int[] array = new int[1000];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
test(true, a, array, true, false);
|
||||
test(false, b, array, true, false);
|
||||
test(false, d, array, true, true);
|
||||
test(true, a, array, false, false);
|
||||
test(false, b, array, false, false);
|
||||
testHelper2(42);
|
||||
testHelper3(true, 42);
|
||||
}
|
||||
for (int j = 0; j < 1000; j++) {
|
||||
for (int i = 0; i < 1_000_000; i++) {
|
||||
test(false, d, array, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int test(boolean flag, Object o, float[] array, boolean flag2, boolean flag3) {
|
||||
int ret = (int)array[2];
|
||||
if (o == null) {
|
||||
}
|
||||
saved_o = o; // (CastPP o): cast to not null
|
||||
|
||||
// A.objectField load from o hosted here even though o was not checked to be of type A
|
||||
// result of the load doesn't hold an oop if o is not an A
|
||||
if (flag2) {
|
||||
for (int i = 1; i < 100; i *= 2) {
|
||||
// safepoint here with result of load above live and expected to be an oop. Not the case
|
||||
// if o is of type D: crash in gc code
|
||||
}
|
||||
|
||||
if (flag3) {
|
||||
} else {
|
||||
saved_casted_o = (A) o; // (CheckCastPP (CastPP o)): cast to not null A
|
||||
|
||||
int j;
|
||||
for (j = 1; j < 2; j *= 2) {
|
||||
|
||||
}
|
||||
|
||||
testHelper3(flag, j); // goes away after CCP
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
}
|
||||
// array[2] after one round of loop opts, control
|
||||
// dependent on range check, range check replaced by
|
||||
// array[2] range check above, control dependent
|
||||
// nodes become control dependent on that range check
|
||||
ret += array[i];
|
||||
|
||||
Object o2;
|
||||
if (flag) {
|
||||
o2 = saved_casted_o; // (CheckCastPP (CastPP o)): cast to to not null A
|
||||
} else {
|
||||
o2 = testHelper2(i); // (CastPP o) after 1 round of loop opts: cast to not null
|
||||
}
|
||||
// subtype check split thru Phi. CheckCastPP becomes control dependent on merge point
|
||||
// phi becomes (CastPP o) after 1 round of loop opts: cast to not null
|
||||
// subtype check from split thru phi in one branch of the if replaced by dominating one
|
||||
// empty if blocks, if goes away. CheckCastPP becomes control dependent on range check above
|
||||
// CastPP replaced by dominating CastPP for null check
|
||||
A a = (A) o2;
|
||||
ret += a.objectField.intField;
|
||||
}
|
||||
} else {
|
||||
// same logic as above so if this a.objectField load and
|
||||
// the one above lose their dependency on the type check
|
||||
// they common above all ifs
|
||||
saved_casted_o = (A) o;
|
||||
|
||||
int j;
|
||||
for (j = 1; j < 2; j *= 2) {
|
||||
|
||||
}
|
||||
|
||||
testHelper3(flag, j);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
}
|
||||
ret += array[i];
|
||||
|
||||
Object o2;
|
||||
if (flag) {
|
||||
o2 = saved_casted_o;
|
||||
} else {
|
||||
o2 = testHelper2(i);
|
||||
}
|
||||
A a = (A) o2;
|
||||
ret += a.objectField.intField;
|
||||
ret += barrier;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void testHelper3(boolean flag, int j) {
|
||||
if (j == 2) {
|
||||
if (flag) {
|
||||
barrier = 42;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object testHelper2(int i) {
|
||||
Object o2;
|
||||
if (i == 2) {
|
||||
o2 = saved_o;
|
||||
} else {
|
||||
o2 = field_o;
|
||||
if (o2 == null) {
|
||||
}
|
||||
}
|
||||
return o2;
|
||||
}
|
||||
|
||||
private static class C {
|
||||
}
|
||||
|
||||
private static class A extends C {
|
||||
public E objectField = new E();
|
||||
}
|
||||
|
||||
private static class B extends A {
|
||||
}
|
||||
|
||||
private static class D extends C {
|
||||
public int neverAccessedField = 0x12345678;
|
||||
|
||||
}
|
||||
|
||||
private static class E {
|
||||
public int intField;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user