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.
|
// If input is already higher or equal to cast type, then this is an identity.
|
||||||
Node* ConstraintCastNode::Identity(PhaseGVN* phase) {
|
Node* ConstraintCastNode::Identity(PhaseGVN* phase) {
|
||||||
|
if (_dependency == UnconditionalDependency) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
Node* dom = dominating_cast(phase, phase);
|
Node* dom = dominating_cast(phase, phase);
|
||||||
if (dom != nullptr) {
|
if (dom != nullptr) {
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
if (_dependency != RegularDependency) {
|
return higher_equal_types(phase, in(1)) ? in(1) : this;
|
||||||
return this;
|
|
||||||
}
|
|
||||||
return phase->type(in(1))->higher_equal_speculative(_type) ? in(1) : this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------Value------------------------------------------
|
//------------------------------Value------------------------------------------
|
||||||
@ -101,47 +101,62 @@ Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||||||
return (in(0) && remove_dead_region(phase, can_reshape)) ? this : nullptr;
|
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 {
|
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 {
|
uint ConstraintCastNode::size_of() const {
|
||||||
return sizeof(*this);
|
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) {
|
switch(opcode) {
|
||||||
case Op_CastII: {
|
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);
|
cast->set_req(0, c);
|
||||||
return cast;
|
return cast;
|
||||||
}
|
}
|
||||||
case Op_CastLL: {
|
case Op_CastLL: {
|
||||||
Node* cast = new CastLLNode(n, t, dependency);
|
Node* cast = new CastLLNode(n, t, dependency, extra_types);
|
||||||
cast->set_req(0, c);
|
cast->set_req(0, c);
|
||||||
return cast;
|
return cast;
|
||||||
}
|
}
|
||||||
case Op_CastPP: {
|
case Op_CastPP: {
|
||||||
Node* cast = new CastPPNode(n, t, dependency);
|
Node* cast = new CastPPNode(n, t, dependency, extra_types);
|
||||||
cast->set_req(0, c);
|
cast->set_req(0, c);
|
||||||
return cast;
|
return cast;
|
||||||
}
|
}
|
||||||
case Op_CastFF: {
|
case Op_CastFF: {
|
||||||
Node* cast = new CastFFNode(n, t, dependency);
|
Node* cast = new CastFFNode(n, t, dependency, extra_types);
|
||||||
cast->set_req(0, c);
|
cast->set_req(0, c);
|
||||||
return cast;
|
return cast;
|
||||||
}
|
}
|
||||||
case Op_CastDD: {
|
case Op_CastDD: {
|
||||||
Node* cast = new CastDDNode(n, t, dependency);
|
Node* cast = new CastDDNode(n, t, dependency, extra_types);
|
||||||
cast->set_req(0, c);
|
cast->set_req(0, c);
|
||||||
return cast;
|
return cast;
|
||||||
}
|
}
|
||||||
case Op_CastVV: {
|
case Op_CastVV: {
|
||||||
Node* cast = new CastVVNode(n, t, dependency);
|
Node* cast = new CastVVNode(n, t, dependency, extra_types);
|
||||||
cast->set_req(0, c);
|
cast->set_req(0, c);
|
||||||
return cast;
|
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:
|
default:
|
||||||
fatal("Bad opcode %d", opcode);
|
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) {
|
Node* ConstraintCastNode::make(Node* c, Node *n, const Type *t, DependencyType dependency, BasicType bt) {
|
||||||
switch(bt) {
|
switch(bt) {
|
||||||
case T_INT: {
|
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: {
|
case T_LONG: {
|
||||||
return make_cast(Op_CastLL, c, n, t, dependency);
|
return make_cast(Op_CastLL, c, n, t, dependency, nullptr);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fatal("Bad basic type %s", type2name(bt));
|
fatal("Bad basic type %s", type2name(bt));
|
||||||
@ -187,7 +202,7 @@ TypeNode* ConstraintCastNode::dominating_cast(PhaseGVN* gvn, PhaseTransform* pt)
|
|||||||
u->outcnt() > 0 &&
|
u->outcnt() > 0 &&
|
||||||
u->Opcode() == opc &&
|
u->Opcode() == opc &&
|
||||||
u->in(0) != nullptr &&
|
u->in(0) != nullptr &&
|
||||||
u->bottom_type()->higher_equal(type())) {
|
higher_equal_types(gvn, u)) {
|
||||||
if (pt->is_dominator(u->in(0), ctl)) {
|
if (pt->is_dominator(u->in(0), ctl)) {
|
||||||
return u->as_Type();
|
return u->as_Type();
|
||||||
}
|
}
|
||||||
@ -203,9 +218,28 @@ TypeNode* ConstraintCastNode::dominating_cast(PhaseGVN* gvn, PhaseTransform* pt)
|
|||||||
return nullptr;
|
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
|
#ifndef PRODUCT
|
||||||
void ConstraintCastNode::dump_spec(outputStream *st) const {
|
void ConstraintCastNode::dump_spec(outputStream *st) const {
|
||||||
TypeNode::dump_spec(st);
|
TypeNode::dump_spec(st);
|
||||||
|
if (_extra_types != nullptr) {
|
||||||
|
st->print(" extra types: ");
|
||||||
|
_extra_types->dump_on(st);
|
||||||
|
}
|
||||||
if (_dependency != RegularDependency) {
|
if (_dependency != RegularDependency) {
|
||||||
st->print(" %s dependency", _dependency == StrongDependency ? "strong" : "unconditional");
|
st->print(" %s dependency", _dependency == StrongDependency ? "strong" : "unconditional");
|
||||||
}
|
}
|
||||||
@ -524,20 +558,21 @@ Node* CastP2XNode::Identity(PhaseGVN* phase) {
|
|||||||
return this;
|
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;
|
Node* cast= nullptr;
|
||||||
if (type->isa_int()) {
|
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()) {
|
} 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()) {
|
} 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()) {
|
} 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()) {
|
} 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()) {
|
} 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;
|
return cast;
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,20 @@ public:
|
|||||||
const DependencyType _dependency;
|
const DependencyType _dependency;
|
||||||
virtual bool cmp( const Node &n ) const;
|
virtual bool cmp( const Node &n ) const;
|
||||||
virtual uint size_of() 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;
|
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:
|
public:
|
||||||
ConstraintCastNode(Node *n, const Type *t, DependencyType dependency)
|
ConstraintCastNode(Node* n, const Type* t, ConstraintCastNode::DependencyType dependency,
|
||||||
: TypeNode(t,2), _dependency(dependency) {
|
const TypeTuple* extra_types)
|
||||||
|
: TypeNode(t,2), _dependency(dependency), _extra_types(extra_types) {
|
||||||
init_class_id(Class_ConstraintCast);
|
init_class_id(Class_ConstraintCast);
|
||||||
init_req(1, n);
|
init_req(1, n);
|
||||||
}
|
}
|
||||||
@ -59,14 +68,15 @@ public:
|
|||||||
virtual bool depends_only_on_test() const { return _dependency == RegularDependency; }
|
virtual bool depends_only_on_test() const { return _dependency == RegularDependency; }
|
||||||
bool carry_dependency() const { return _dependency != RegularDependency; }
|
bool carry_dependency() const { return _dependency != RegularDependency; }
|
||||||
TypeNode* dominating_cast(PhaseGVN* gvn, PhaseTransform* pt) const;
|
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);
|
static Node* make(Node* c, Node *n, const Type *t, DependencyType dependency, BasicType bt);
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
virtual void dump_spec(outputStream *st) const;
|
virtual void dump_spec(outputStream *st) const;
|
||||||
#endif
|
#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);
|
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-------------------------------------
|
//------------------------------CastIINode-------------------------------------
|
||||||
@ -103,12 +123,12 @@ class CastIINode: public ConstraintCastNode {
|
|||||||
virtual uint size_of() const;
|
virtual uint size_of() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CastIINode(Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false)
|
CastIINode(Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false, const TypeTuple* types = nullptr)
|
||||||
: ConstraintCastNode(n, t, dependency), _range_check_dependency(range_check_dependency) {
|
: ConstraintCastNode(n, t, dependency, types), _range_check_dependency(range_check_dependency) {
|
||||||
init_class_id(Class_CastII);
|
init_class_id(Class_CastII);
|
||||||
}
|
}
|
||||||
CastIINode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false)
|
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_class_id(Class_CastII);
|
||||||
init_req(0, ctrl);
|
init_req(0, ctrl);
|
||||||
}
|
}
|
||||||
@ -134,12 +154,12 @@ class CastIINode: public ConstraintCastNode {
|
|||||||
class CastLLNode: public ConstraintCastNode {
|
class CastLLNode: public ConstraintCastNode {
|
||||||
public:
|
public:
|
||||||
CastLLNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
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_class_id(Class_CastLL);
|
||||||
init_req(0, ctrl);
|
init_req(0, ctrl);
|
||||||
}
|
}
|
||||||
CastLLNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
CastLLNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||||
: ConstraintCastNode(n, t, dependency){
|
: ConstraintCastNode(n, t, dependency, types) {
|
||||||
init_class_id(Class_CastLL);
|
init_class_id(Class_CastLL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,8 +171,8 @@ public:
|
|||||||
|
|
||||||
class CastFFNode: public ConstraintCastNode {
|
class CastFFNode: public ConstraintCastNode {
|
||||||
public:
|
public:
|
||||||
CastFFNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
CastFFNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||||
: ConstraintCastNode(n, t, dependency){
|
: ConstraintCastNode(n, t, dependency, types) {
|
||||||
init_class_id(Class_CastFF);
|
init_class_id(Class_CastFF);
|
||||||
}
|
}
|
||||||
virtual int Opcode() const;
|
virtual int Opcode() const;
|
||||||
@ -161,8 +181,8 @@ public:
|
|||||||
|
|
||||||
class CastDDNode: public ConstraintCastNode {
|
class CastDDNode: public ConstraintCastNode {
|
||||||
public:
|
public:
|
||||||
CastDDNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
CastDDNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||||
: ConstraintCastNode(n, t, dependency){
|
: ConstraintCastNode(n, t, dependency, types) {
|
||||||
init_class_id(Class_CastDD);
|
init_class_id(Class_CastDD);
|
||||||
}
|
}
|
||||||
virtual int Opcode() const;
|
virtual int Opcode() const;
|
||||||
@ -171,8 +191,8 @@ public:
|
|||||||
|
|
||||||
class CastVVNode: public ConstraintCastNode {
|
class CastVVNode: public ConstraintCastNode {
|
||||||
public:
|
public:
|
||||||
CastVVNode(Node* n, const Type* t, DependencyType dependency = RegularDependency)
|
CastVVNode(Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||||
: ConstraintCastNode(n, t, dependency){
|
: ConstraintCastNode(n, t, dependency, types) {
|
||||||
init_class_id(Class_CastVV);
|
init_class_id(Class_CastVV);
|
||||||
}
|
}
|
||||||
virtual int Opcode() const;
|
virtual int Opcode() const;
|
||||||
@ -184,8 +204,8 @@ public:
|
|||||||
// cast pointer to pointer (different type)
|
// cast pointer to pointer (different type)
|
||||||
class CastPPNode: public ConstraintCastNode {
|
class CastPPNode: public ConstraintCastNode {
|
||||||
public:
|
public:
|
||||||
CastPPNode (Node *n, const Type *t, DependencyType dependency = RegularDependency)
|
CastPPNode (Node *n, const Type *t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||||
: ConstraintCastNode(n, t, dependency) {
|
: ConstraintCastNode(n, t, dependency, types) {
|
||||||
}
|
}
|
||||||
virtual int Opcode() const;
|
virtual int Opcode() const;
|
||||||
virtual uint ideal_reg() const { return Op_RegP; }
|
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,
|
// for _checkcast, cast pointer to pointer (different type), without JOIN,
|
||||||
class CheckCastPPNode: public ConstraintCastNode {
|
class CheckCastPPNode: public ConstraintCastNode {
|
||||||
public:
|
public:
|
||||||
CheckCastPPNode(Node *c, Node *n, const Type *t, DependencyType dependency = RegularDependency)
|
CheckCastPPNode(Node *c, Node *n, const Type *t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
|
||||||
: ConstraintCastNode(n, t, dependency) {
|
: ConstraintCastNode(n, t, dependency, types) {
|
||||||
init_class_id(Class_CheckCastPP);
|
init_class_id(Class_CheckCastPP);
|
||||||
init_req(0, c);
|
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
|
// Add casts to carry the control dependency of the Phi that is
|
||||||
// going away
|
// going away
|
||||||
Node* cast = nullptr;
|
Node* cast = nullptr;
|
||||||
|
const TypeTuple* extra_types = collect_types(phase);
|
||||||
if (phi_type->isa_ptr()) {
|
if (phi_type->isa_ptr()) {
|
||||||
const Type* uin_type = phase->type(uin);
|
const Type* uin_type = phase->type(uin);
|
||||||
if (!phi_type->isa_oopptr() && !uin_type->isa_oopptr()) {
|
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 {
|
} else {
|
||||||
// Use a CastPP for a cast to not null and a CheckCastPP for
|
// 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
|
// 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
|
// null, uin's type must be casted to not null
|
||||||
if (phi_type->join(TypePtr::NOTNULL) == phi_type->remove_speculative() &&
|
if (phi_type->join(TypePtr::NOTNULL) == phi_type->remove_speculative() &&
|
||||||
uin_type->join(TypePtr::NOTNULL) != uin_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,
|
// 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);
|
cast = phase->transform(cast);
|
||||||
n = 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) {
|
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 {
|
} 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");
|
assert(cast != nullptr, "cast should be set");
|
||||||
cast = phase->transform(cast);
|
cast = phase->transform(cast);
|
||||||
@ -2560,6 +2565,52 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||||||
return progress; // Return any progress
|
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* PhiNode::clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn) {
|
||||||
Node_Stack stack(1);
|
Node_Stack stack(1);
|
||||||
VectorSet visited;
|
VectorSet visited;
|
||||||
|
@ -268,6 +268,8 @@ public:
|
|||||||
#else //ASSERT
|
#else //ASSERT
|
||||||
void verify_adr_type(bool recursive = false) const {}
|
void verify_adr_type(bool recursive = false) const {}
|
||||||
#endif //ASSERT
|
#endif //ASSERT
|
||||||
|
|
||||||
|
const TypeTuple* collect_types(PhaseGVN* phase) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------GotoNode---------------------------------------
|
//------------------------------GotoNode---------------------------------------
|
||||||
|
@ -1728,7 +1728,8 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
|
|||||||
Node* in = x->in(k);
|
Node* in = x->in(k);
|
||||||
if (in != nullptr && n_loop->is_member(get_loop(get_ctrl(in)))) {
|
if (in != nullptr && n_loop->is_member(get_loop(get_ctrl(in)))) {
|
||||||
const Type* in_t = _igvn.type(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) {
|
if (cast != nullptr) {
|
||||||
Node* prev = _igvn.hash_find_insert(cast);
|
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…
x
Reference in New Issue
Block a user