Skip to content

Commit

Permalink
8297582: C2: very slow compilation due to type system verification code
Browse files Browse the repository at this point in the history
Reviewed-by: kvn, vlivanov
  • Loading branch information
rwestrel committed Feb 2, 2023
1 parent 8d6e8a4 commit af474ce
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/hotspot/share/opto/compile.cpp
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/opto/compile.hpp
Expand Up @@ -92,6 +92,7 @@ class UnstableIfTrap;
class nmethod;
class Node_Stack;
struct Final_Reshape_Counts;
class VerifyMeetResult;

enum LoopOptsMode {
LoopOptsDefault,
Expand Down Expand Up @@ -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
Expand Down
188 changes: 164 additions & 24 deletions src/hotspot/share/opto/type.cpp
Expand Up @@ -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();
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
}
Expand Down
6 changes: 5 additions & 1 deletion src/hotspot/share/opto/type.hpp
Expand Up @@ -70,6 +70,7 @@ class TypeKlassPtr;
class TypeInstKlassPtr;
class TypeAryKlassPtr;
class TypeMetadataPtr;
class VerifyMeet;

//------------------------------Type-------------------------------------------
// Basic Type object, represents a set of primitive Values.
Expand Down Expand Up @@ -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.
Expand All @@ -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() {
Expand Down
60 changes: 60 additions & 0 deletions 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][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][];
}
}

1 comment on commit af474ce

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.