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:
Roland Westrelin 2023-09-26 06:55:01 +00:00
parent 9e6cb62048
commit 52983ed529
8 changed files with 496 additions and 49 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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---------------------------------------

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}