diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 9548b3347bfc8..ef57f254a8ace 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -978,9 +978,9 @@ void Compile::Init(bool aliasing) { _immutable_memory = NULL; // filled in at first inquiry #ifdef ASSERT - _type_verify_symmetry = true; _phase_optimize_finished = false; _exception_backedge = false; + _type_verify = NULL; #endif // Globally visible Nodes diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index f7f99ef682cca..c486a36828ae2 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -92,6 +92,7 @@ class UnstableIfTrap; class nmethod; class Node_Stack; struct Final_Reshape_Counts; +class VerifyMeetResult; enum LoopOptsMode { LoopOptsDefault, @@ -1211,7 +1212,7 @@ class Compile : public Phase { bool in_24_bit_fp_mode() const { return _in_24_bit_fp_mode; } #endif // IA32 #ifdef ASSERT - bool _type_verify_symmetry; + VerifyMeetResult* _type_verify; void set_exception_backedge() { _exception_backedge = true; } bool has_exception_backedge() const { return _exception_backedge; } #endif diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 08ebb8ceed903..0534544bf0fae 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -786,9 +786,155 @@ bool Type::is_nan() const { return false; } -void Type::check_symmetrical(const Type* t, const Type* mt) const { #ifdef ASSERT - const Type* mt2 = t->xmeet(this); +class VerifyMeet; +class VerifyMeetResult : public ArenaObj { + friend class VerifyMeet; + friend class Type; +private: + class VerifyMeetResultEntry { + private: + const Type* _in1; + const Type* _in2; + const Type* _res; + public: + VerifyMeetResultEntry(const Type* in1, const Type* in2, const Type* res): + _in1(in1), _in2(in2), _res(res) { + } + VerifyMeetResultEntry(): + _in1(NULL), _in2(NULL), _res(NULL) { + } + + bool operator==(const VerifyMeetResultEntry& rhs) const { + return _in1 == rhs._in1 && + _in2 == rhs._in2 && + _res == rhs._res; + } + + bool operator!=(const VerifyMeetResultEntry& rhs) const { + return !(rhs == *this); + } + + static int compare(const VerifyMeetResultEntry& v1, const VerifyMeetResultEntry& v2) { + if ((intptr_t) v1._in1 < (intptr_t) v2._in1) { + return -1; + } else if (v1._in1 == v2._in1) { + if ((intptr_t) v1._in2 < (intptr_t) v2._in2) { + return -1; + } else if (v1._in2 == v2._in2) { + assert(v1._res == v2._res || v1._res == NULL || v2._res == NULL, "same inputs should lead to same result"); + return 0; + } + return 1; + } + return 1; + } + const Type* res() const { return _res; } + }; + uint _depth; + GrowableArray<VerifyMeetResultEntry> _cache; + + // With verification code, the meet of A and B causes the computation of: + // 1- meet(A, B) + // 2- meet(B, A) + // 3- meet(dual(meet(A, B)), dual(A)) + // 4- meet(dual(meet(A, B)), dual(B)) + // 5- meet(dual(A), dual(B)) + // 6- meet(dual(B), dual(A)) + // 7- meet(dual(meet(dual(A), dual(B))), A) + // 8- meet(dual(meet(dual(A), dual(B))), B) + // + // In addition the meet of A[] and B[] requires the computation of the meet of A and B. + // + // The meet of A[] and B[] triggers the computation of: + // 1- meet(A[], B[][) + // 1.1- meet(A, B) + // 1.2- meet(B, A) + // 1.3- meet(dual(meet(A, B)), dual(A)) + // 1.4- meet(dual(meet(A, B)), dual(B)) + // 1.5- meet(dual(A), dual(B)) + // 1.6- meet(dual(B), dual(A)) + // 1.7- meet(dual(meet(dual(A), dual(B))), A) + // 1.8- meet(dual(meet(dual(A), dual(B))), B) + // 2- meet(B[], A[]) + // 2.1- meet(B, A) = 1.2 + // 2.2- meet(A, B) = 1.1 + // 2.3- meet(dual(meet(B, A)), dual(B)) = 1.4 + // 2.4- meet(dual(meet(B, A)), dual(A)) = 1.3 + // 2.5- meet(dual(B), dual(A)) = 1.6 + // 2.6- meet(dual(A), dual(B)) = 1.5 + // 2.7- meet(dual(meet(dual(B), dual(A))), B) = 1.8 + // 2.8- meet(dual(meet(dual(B), dual(A))), B) = 1.7 + // etc. + // The number of meet operations performed grows exponentially with the number of dimensions of the arrays but the number + // of different meet operations is linear in the number of dimensions. The function below caches meet results for the + // duration of the meet at the root of the recursive calls. + // + const Type* meet(const Type* t1, const Type* t2) { + bool found = false; + const VerifyMeetResultEntry meet(t1, t2, NULL); + int pos = _cache.find_sorted<VerifyMeetResultEntry, VerifyMeetResultEntry::compare>(meet, found); + const Type* res = NULL; + if (found) { + res = _cache.at(pos).res(); + } else { + res = t1->xmeet(t2); + _cache.insert_sorted<VerifyMeetResultEntry::compare>(VerifyMeetResultEntry(t1, t2, res)); + found = false; + _cache.find_sorted<VerifyMeetResultEntry, VerifyMeetResultEntry::compare>(meet, found); + assert(found, "should be in table after it's added"); + } + return res; + } + + void add(const Type* t1, const Type* t2, const Type* res) { + _cache.insert_sorted<VerifyMeetResultEntry::compare>(VerifyMeetResultEntry(t1, t2, res)); + } + + bool empty_cache() const { + return _cache.length() == 0; + } +public: + VerifyMeetResult(Compile* C) : + _depth(0), _cache(C->comp_arena(), 2, 0, VerifyMeetResultEntry()) { + } +}; + +void Type::assert_type_verify_empty() const { + assert(Compile::current()->_type_verify == NULL || Compile::current()->_type_verify->empty_cache(), "cache should have been discarded"); +} + +class VerifyMeet { +private: + Compile* _C; +public: + VerifyMeet(Compile* C) : _C(C) { + if (C->_type_verify == NULL) { + C->_type_verify = new (C->comp_arena())VerifyMeetResult(C); + } + _C->_type_verify->_depth++; + } + + ~VerifyMeet() { + assert(_C->_type_verify->_depth != 0, ""); + _C->_type_verify->_depth--; + if (_C->_type_verify->_depth == 0) { + _C->_type_verify->_cache.trunc_to(0); + } + } + + const Type* meet(const Type* t1, const Type* t2) const { + return _C->_type_verify->meet(t1, t2); + } + + void add(const Type* t1, const Type* t2, const Type* res) const { + _C->_type_verify->add(t1, t2, res); + } +}; + +void Type::check_symmetrical(const Type* t, const Type* mt, const VerifyMeet& verify) const { + Compile* C = Compile::current(); + const Type* mt2 = verify.meet(t, this); if (mt != mt2) { tty->print_cr("=== Meet Not Commutative ==="); tty->print("t = "); t->dump(); tty->cr(); @@ -798,8 +944,8 @@ void Type::check_symmetrical(const Type* t, const Type* mt) const { fatal("meet not commutative"); } const Type* dual_join = mt->_dual; - const Type* t2t = dual_join->xmeet(t->_dual); - const Type* t2this = dual_join->xmeet(this->_dual); + const Type* t2t = verify.meet(dual_join,t->_dual); + const Type* t2this = verify.meet(dual_join,this->_dual); // Interface meet Oop is Not Symmetric: // Interface:AnyNull meet Oop:AnyNull == Interface:AnyNull @@ -820,8 +966,8 @@ void Type::check_symmetrical(const Type* t, const Type* mt) const { fatal("meet not symmetric"); } -#endif } +#endif //------------------------------meet------------------------------------------- // Compute the MEET of two types. NOT virtual. It enforces that meet is @@ -836,32 +982,26 @@ const Type *Type::meet_helper(const Type *t, bool include_speculative) const { return result->make_narrowklass(); } +#ifdef ASSERT + Compile* C = Compile::current(); + VerifyMeet verify(C); +#endif + const Type *this_t = maybe_remove_speculative(include_speculative); t = t->maybe_remove_speculative(include_speculative); const Type *mt = this_t->xmeet(t); #ifdef ASSERT - if (isa_narrowoop() || t->isa_narrowoop()) return mt; - if (isa_narrowklass() || t->isa_narrowklass()) return mt; - Compile* C = Compile::current(); - if (!C->_type_verify_symmetry) { + verify.add(this_t, t, mt); + if (isa_narrowoop() || t->isa_narrowoop()) { + return mt; + } + if (isa_narrowklass() || t->isa_narrowklass()) { return mt; } - this_t->check_symmetrical(t, mt); - // In the case of an array, computing the meet above, caused the - // computation of the meet of the elements which at verification - // time caused the computation of the meet of the dual of the - // elements. Computing the meet of the dual of the arrays here - // causes the meet of the dual of the elements to be computed which - // would cause the meet of the dual of the dual of the elements, - // that is the meet of the elements already computed above to be - // computed. Avoid redundant computations by requesting no - // verification. - C->_type_verify_symmetry = false; - const Type *mt_dual = this_t->_dual->xmeet(t->_dual); - this_t->_dual->check_symmetrical(t->_dual, mt_dual); - assert(!C->_type_verify_symmetry, "shouldn't have changed"); - C->_type_verify_symmetry = true; + this_t->check_symmetrical(t, mt, verify); + const Type *mt_dual = verify.meet(this_t->_dual, t->_dual); + this_t->_dual->check_symmetrical(t->_dual, mt_dual, verify); #endif return mt; } diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 3aadd24992e8b..2528e26b161de 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -70,6 +70,7 @@ class TypeKlassPtr; class TypeInstKlassPtr; class TypeAryKlassPtr; class TypeMetadataPtr; +class VerifyMeet; //------------------------------Type------------------------------------------- // Basic Type object, represents a set of primitive Values. @@ -170,7 +171,7 @@ class Type { const Type *meet_helper(const Type *t, bool include_speculative) const; - void check_symmetrical(const Type *t, const Type *mt) const; + void check_symmetrical(const Type* t, const Type* mt, const VerifyMeet& verify) const NOT_DEBUG_RETURN; protected: // Each class of type is also identified by its base. @@ -181,9 +182,12 @@ class Type { const Type *hashcons(); // Hash-cons the type virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; const Type *join_helper(const Type *t, bool include_speculative) const { + assert_type_verify_empty(); return dual()->meet_helper(t->dual(), include_speculative)->dual(); } + void assert_type_verify_empty() const NOT_DEBUG_RETURN; + public: inline void* operator new( size_t x ) throw() { diff --git a/test/hotspot/jtreg/compiler/types/TestArrayManyDimensions.java b/test/hotspot/jtreg/compiler/types/TestArrayManyDimensions.java new file mode 100644 index 0000000000000..174790f5d795b --- /dev/null +++ b/test/hotspot/jtreg/compiler/types/TestArrayManyDimensions.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022, 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 8297582 + * @summary C2: very slow compilation due to type system verification code + * + * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation TestArrayManyDimensions + * + */ + +import java.util.function.IntFunction; + +public class TestArrayManyDimensions { + + static class A { + + } + + static class B extends A { + } + + public static void main(String[] args) { + final IntFunction<String[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]> lambda = String[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]::new; + for (int i = 0; i < 20_000; i++) { + test1(); + lambda.apply(2); + test2(); + } + } + + private static String[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] test1() { + return new String[2][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]; + } + + private static A[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] test2() { + return new B[2][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]; + } +}