diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 8d67f4db00f..7168cdcf7fe 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -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; } diff --git a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp index eb9bcaa54b1..d3a41115ac4 100644 --- a/src/hotspot/share/opto/castnode.hpp +++ b/src/hotspot/share/opto/castnode.hpp @@ -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); } diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index fae10177676..0221fe0c3d0 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -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 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(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(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; diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index c008c34c4b0..81ac180dcfa 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -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--------------------------------------- diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index be91a1b4be5..cc006d5481b 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -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); diff --git a/test/hotspot/jtreg/compiler/controldependency/TestAddPChainMismatchedBase.java b/test/hotspot/jtreg/compiler/controldependency/TestAddPChainMismatchedBase.java new file mode 100644 index 00000000000..15c1c7c43de --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestAddPChainMismatchedBase.java @@ -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; + } +} diff --git a/test/hotspot/jtreg/compiler/controldependency/TestAddPChainMismatchedBase2.java b/test/hotspot/jtreg/compiler/controldependency/TestAddPChainMismatchedBase2.java new file mode 100644 index 00000000000..312dfbf3eb3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestAddPChainMismatchedBase2.java @@ -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); + } +} diff --git a/test/hotspot/jtreg/compiler/controldependency/TestLoadBypassesClassCast.java b/test/hotspot/jtreg/compiler/controldependency/TestLoadBypassesClassCast.java new file mode 100644 index 00000000000..f5608c3a51a --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestLoadBypassesClassCast.java @@ -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; + } + +}