mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-20 21:03:18 +00:00
8266074: Vtable-based CHA implementation
Reviewed-by: kvn, jrose, dlong
This commit is contained in:
parent
347d41df90
commit
127bfe44f7
@ -2026,7 +2026,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
|
||||
// by dynamic class loading. Be sure to test the "static" receiver
|
||||
// dest_method here, as opposed to the actual receiver, which may
|
||||
// falsely lead us to believe that the receiver is final or private.
|
||||
dependency_recorder()->assert_unique_concrete_method(actual_recv, cha_monomorphic_target);
|
||||
dependency_recorder()->assert_unique_concrete_method(actual_recv, cha_monomorphic_target, callee_holder, target);
|
||||
}
|
||||
code = Bytecodes::_invokespecial;
|
||||
}
|
||||
|
||||
@ -346,6 +346,9 @@ void BCEscapeAnalyzer::invoke(StateInfo &state, Bytecodes::Code code, ciMethod*
|
||||
(code == Bytecodes::_invokevirtual && !target->is_final_method())) {
|
||||
_dependencies.append(actual_recv);
|
||||
_dependencies.append(inline_target);
|
||||
_dependencies.append(callee_holder);
|
||||
_dependencies.append(target);
|
||||
assert(callee_holder->is_interface() == (code == Bytecodes::_invokeinterface), "sanity");
|
||||
}
|
||||
_dependencies.appendAll(analyzer.dependencies());
|
||||
}
|
||||
@ -1495,9 +1498,11 @@ void BCEscapeAnalyzer::copy_dependencies(Dependencies *deps) {
|
||||
// callee will trigger recompilation.
|
||||
deps->assert_evol_method(method());
|
||||
}
|
||||
for (int i = 0; i < _dependencies.length(); i+=2) {
|
||||
ciKlass *k = _dependencies.at(i)->as_klass();
|
||||
ciMethod *m = _dependencies.at(i+1)->as_method();
|
||||
deps->assert_unique_concrete_method(k, m);
|
||||
for (int i = 0; i < _dependencies.length(); i+=4) {
|
||||
ciKlass* recv_klass = _dependencies.at(i+0)->as_klass();
|
||||
ciMethod* target = _dependencies.at(i+1)->as_method();
|
||||
ciKlass* resolved_klass = _dependencies.at(i+2)->as_klass();
|
||||
ciMethod* resolved_method = _dependencies.at(i+3)->as_method();
|
||||
deps->assert_unique_concrete_method(recv_klass, target, resolved_klass, resolved_method);
|
||||
}
|
||||
}
|
||||
|
||||
@ -714,7 +714,15 @@ ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller,
|
||||
{
|
||||
MutexLocker locker(Compile_lock);
|
||||
InstanceKlass* context = actual_recv->get_instanceKlass();
|
||||
target = methodHandle(THREAD, Dependencies::find_unique_concrete_method(context, root_m->get_Method()));
|
||||
if (UseVtableBasedCHA) {
|
||||
target = methodHandle(THREAD, Dependencies::find_unique_concrete_method(context,
|
||||
root_m->get_Method(),
|
||||
callee_holder->get_Klass(),
|
||||
this->get_Method()));
|
||||
} else {
|
||||
target = methodHandle(THREAD, Dependencies::find_unique_concrete_method(context, root_m->get_Method()));
|
||||
}
|
||||
assert(target() == NULL || !target()->is_abstract(), "not allowed");
|
||||
// %%% Should upgrade this ciMethod API to look for 1 or 2 concrete methods.
|
||||
}
|
||||
|
||||
|
||||
@ -1184,10 +1184,18 @@ void CodeCache::flush_dependents_on(InstanceKlass* dependee) {
|
||||
|
||||
if (number_of_nmethods_with_dependencies() == 0) return;
|
||||
|
||||
KlassDepChange changes(dependee);
|
||||
int marked = 0;
|
||||
if (dependee->is_linked()) {
|
||||
// Class initialization state change.
|
||||
KlassInitDepChange changes(dependee);
|
||||
marked = mark_for_deoptimization(changes);
|
||||
} else {
|
||||
// New class is loaded.
|
||||
NewKlassDepChange changes(dependee);
|
||||
marked = mark_for_deoptimization(changes);
|
||||
}
|
||||
|
||||
// Compute the dependent nmethods
|
||||
if (mark_for_deoptimization(changes) > 0) {
|
||||
if (marked > 0) {
|
||||
// At least one nmethod has been marked for deoptimization
|
||||
Deoptimization::deoptimize_all_marked();
|
||||
}
|
||||
|
||||
@ -103,7 +103,17 @@ void Dependencies::assert_abstract_with_unique_concrete_subtype(ciKlass* ctxk, c
|
||||
void Dependencies::assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm) {
|
||||
check_ctxk(ctxk);
|
||||
check_unique_method(ctxk, uniqm);
|
||||
assert_common_2(unique_concrete_method, ctxk, uniqm);
|
||||
assert_common_2(unique_concrete_method_2, ctxk, uniqm);
|
||||
}
|
||||
|
||||
void Dependencies::assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm, ciKlass* resolved_klass, ciMethod* resolved_method) {
|
||||
check_ctxk(ctxk);
|
||||
check_unique_method(ctxk, uniqm);
|
||||
if (UseVtableBasedCHA) {
|
||||
assert_common_4(unique_concrete_method_4, ctxk, uniqm, resolved_klass, resolved_method);
|
||||
} else {
|
||||
assert_common_2(unique_concrete_method_2, ctxk, uniqm);
|
||||
}
|
||||
}
|
||||
|
||||
void Dependencies::assert_has_no_finalizable_subclasses(ciKlass* ctxk) {
|
||||
@ -166,7 +176,7 @@ void Dependencies::assert_abstract_with_unique_concrete_subtype(Klass* ctxk, Kla
|
||||
void Dependencies::assert_unique_concrete_method(Klass* ctxk, Method* uniqm) {
|
||||
check_ctxk(ctxk);
|
||||
check_unique_method(ctxk, uniqm);
|
||||
assert_common_2(unique_concrete_method, DepValue(_oop_recorder, ctxk), DepValue(_oop_recorder, uniqm));
|
||||
assert_common_2(unique_concrete_method_2, DepValue(_oop_recorder, ctxk), DepValue(_oop_recorder, uniqm));
|
||||
}
|
||||
|
||||
void Dependencies::assert_call_site_target_value(oop call_site, oop method_handle) {
|
||||
@ -247,6 +257,36 @@ void Dependencies::assert_common_2(DepType dept,
|
||||
deps->append(x1);
|
||||
}
|
||||
|
||||
void Dependencies::assert_common_4(DepType dept,
|
||||
ciKlass* ctxk, ciBaseObject* x1, ciBaseObject* x2, ciBaseObject* x3) {
|
||||
assert(has_explicit_context_arg(dept), "sanity");
|
||||
assert(dep_context_arg(dept) == 0, "sanity");
|
||||
assert(dep_args(dept) == 4, "sanity");
|
||||
log_dependency(dept, ctxk, x1, x2, x3);
|
||||
GrowableArray<ciBaseObject*>* deps = _deps[dept];
|
||||
|
||||
// see if the same (or a similar) dep is already recorded
|
||||
if (note_dep_seen(dept, x1) && note_dep_seen(dept, x2) && note_dep_seen(dept, x3)) {
|
||||
// look in this bucket for redundant assertions
|
||||
const int stride = 4;
|
||||
for (int i = deps->length(); (i -= stride) >= 0; ) {
|
||||
ciBaseObject* y1 = deps->at(i+1);
|
||||
ciBaseObject* y2 = deps->at(i+2);
|
||||
ciBaseObject* y3 = deps->at(i+3);
|
||||
if (x1 == y1 && x2 == y2 && x3 == y3) { // same subjects; check the context
|
||||
if (maybe_merge_ctxk(deps, i+0, ctxk)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// append the assertion in the correct bucket:
|
||||
deps->append(ctxk);
|
||||
deps->append(x1);
|
||||
deps->append(x2);
|
||||
deps->append(x3);
|
||||
}
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
bool Dependencies::maybe_merge_ctxk(GrowableArray<DepValue>* deps,
|
||||
int ctxk_i, DepValue ctxk2_dv) {
|
||||
@ -343,6 +383,8 @@ static int sort_dep_arg_2(ciBaseObject** p1, ciBaseObject** p2)
|
||||
{ return sort_dep(p1, p2, 2); }
|
||||
static int sort_dep_arg_3(ciBaseObject** p1, ciBaseObject** p2)
|
||||
{ return sort_dep(p1, p2, 3); }
|
||||
static int sort_dep_arg_4(ciBaseObject** p1, ciBaseObject** p2)
|
||||
{ return sort_dep(p1, p2, 4); }
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
// metadata deps are sorted before object deps
|
||||
@ -386,6 +428,7 @@ void Dependencies::sort_all_deps() {
|
||||
case 1: deps->sort(sort_dep_arg_1, 1); break;
|
||||
case 2: deps->sort(sort_dep_arg_2, 2); break;
|
||||
case 3: deps->sort(sort_dep_arg_3, 3); break;
|
||||
case 4: deps->sort(sort_dep_arg_4, 4); break;
|
||||
default: ShouldNotReachHere(); break;
|
||||
}
|
||||
}
|
||||
@ -413,7 +456,8 @@ size_t Dependencies::estimate_size_in_bytes() {
|
||||
|
||||
ciKlass* Dependencies::ctxk_encoded_as_null(DepType dept, ciBaseObject* x) {
|
||||
switch (dept) {
|
||||
case unique_concrete_method:
|
||||
case unique_concrete_method_2:
|
||||
case unique_concrete_method_4:
|
||||
return x->as_metadata()->as_method()->holder();
|
||||
default:
|
||||
return NULL; // let NULL be NULL
|
||||
@ -423,7 +467,8 @@ ciKlass* Dependencies::ctxk_encoded_as_null(DepType dept, ciBaseObject* x) {
|
||||
Klass* Dependencies::ctxk_encoded_as_null(DepType dept, Metadata* x) {
|
||||
assert(must_be_in_vm(), "raw oops here");
|
||||
switch (dept) {
|
||||
case unique_concrete_method:
|
||||
case unique_concrete_method_2:
|
||||
case unique_concrete_method_4:
|
||||
assert(x->is_method(), "sanity");
|
||||
return ((Method*)x)->method_holder();
|
||||
default:
|
||||
@ -526,7 +571,8 @@ const char* Dependencies::_dep_name[TYPE_LIMIT] = {
|
||||
"evol_method",
|
||||
"leaf_type",
|
||||
"abstract_with_unique_concrete_subtype",
|
||||
"unique_concrete_method",
|
||||
"unique_concrete_method_2",
|
||||
"unique_concrete_method_4",
|
||||
"no_finalizable_subclasses",
|
||||
"call_site_target_value"
|
||||
};
|
||||
@ -536,7 +582,8 @@ int Dependencies::_dep_args[TYPE_LIMIT] = {
|
||||
1, // evol_method m
|
||||
1, // leaf_type ctxk
|
||||
2, // abstract_with_unique_concrete_subtype ctxk, k
|
||||
2, // unique_concrete_method ctxk, m
|
||||
2, // unique_concrete_method_2 ctxk, m
|
||||
4, // unique_concrete_method_4 ctxk, m, resolved_klass, resolved_method
|
||||
1, // no_finalizable_subclasses ctxk
|
||||
2 // call_site_target_value call_site, method_handle
|
||||
};
|
||||
@ -978,7 +1025,7 @@ class AbstractClassHierarchyWalker {
|
||||
static PerfCounter* _perf_find_witness_in_calls_count;
|
||||
|
||||
protected:
|
||||
virtual Klass* find_witness_in(KlassDepChange* changes) = 0;
|
||||
virtual Klass* find_witness_in(KlassDepChange& changes) = 0;
|
||||
virtual Klass* find_witness_anywhere(InstanceKlass* context_type) = 0;
|
||||
|
||||
AbstractClassHierarchyWalker(Klass* participant) : _record_witnesses(0), _num_participants(0)
|
||||
@ -1112,7 +1159,7 @@ Klass* AbstractClassHierarchyWalker::find_witness(InstanceKlass* context_type, K
|
||||
if (UsePerfData) {
|
||||
_perf_find_witness_in_calls_count->inc();
|
||||
}
|
||||
return find_witness_in(changes);
|
||||
return find_witness_in(*changes);
|
||||
} else {
|
||||
if (UsePerfData) {
|
||||
_perf_find_witness_anywhere_calls_count->inc();
|
||||
@ -1126,7 +1173,7 @@ class ConcreteSubtypeFinder : public AbstractClassHierarchyWalker {
|
||||
bool is_witness(Klass* k);
|
||||
|
||||
protected:
|
||||
virtual Klass* find_witness_in(KlassDepChange* changes);
|
||||
virtual Klass* find_witness_in(KlassDepChange& changes);
|
||||
virtual Klass* find_witness_anywhere(InstanceKlass* context_type);
|
||||
|
||||
public:
|
||||
@ -1141,15 +1188,15 @@ bool ConcreteSubtypeFinder::is_witness(Klass* k) {
|
||||
}
|
||||
}
|
||||
|
||||
Klass* ConcreteSubtypeFinder::find_witness_in(KlassDepChange* changes) {
|
||||
Klass* ConcreteSubtypeFinder::find_witness_in(KlassDepChange& changes) {
|
||||
// When looking for unexpected concrete types, do not look beneath expected ones:
|
||||
// * CX > CC > C' is OK, even if C' is new.
|
||||
// * CX > { CC, C' } is not OK if C' is new, and C' is the witness.
|
||||
Klass* new_type = changes->new_type();
|
||||
Klass* new_type = changes.as_new_klass_change()->new_type();
|
||||
assert(!is_participant(new_type), "only old classes are participants");
|
||||
// If the new type is a subtype of a participant, we are done.
|
||||
for (uint i = 0; i < num_participants(); i++) {
|
||||
if (changes->involves_context(participant(i))) {
|
||||
if (changes.involves_context(participant(i))) {
|
||||
// new guy is protected from this check by previous participant
|
||||
return NULL;
|
||||
}
|
||||
@ -1187,7 +1234,7 @@ class ConcreteMethodFinder : public AbstractClassHierarchyWalker {
|
||||
bool is_witness(Klass* k);
|
||||
|
||||
protected:
|
||||
virtual Klass* find_witness_in(KlassDepChange* changes);
|
||||
virtual Klass* find_witness_in(KlassDepChange& changes);
|
||||
virtual Klass* find_witness_anywhere(InstanceKlass* context_type);
|
||||
|
||||
bool witnessed_reabstraction_in_supers(Klass* k);
|
||||
@ -1286,10 +1333,10 @@ bool ConcreteMethodFinder::is_witness(Klass* k) {
|
||||
}
|
||||
}
|
||||
|
||||
Klass* ConcreteMethodFinder::find_witness_in(KlassDepChange* changes) {
|
||||
Klass* ConcreteMethodFinder::find_witness_in(KlassDepChange& changes) {
|
||||
// When looking for unexpected concrete methods, look beneath expected ones, to see if there are overrides.
|
||||
// * CX.m > CC.m > C'.m is not OK, if C'.m is new, and C' is the witness.
|
||||
Klass* new_type = changes->new_type();
|
||||
Klass* new_type = changes.as_new_klass_change()->new_type();
|
||||
assert(!is_participant(new_type), "only old classes are participants");
|
||||
if (is_witness(new_type)) {
|
||||
return new_type;
|
||||
@ -1342,6 +1389,175 @@ Klass* ConcreteMethodFinder::find_witness_anywhere(InstanceKlass* context_type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// For some method m and some class ctxk (subclass of method holder),
|
||||
// enumerate all distinct overrides of m in concrete subclasses of ctxk.
|
||||
// It relies on vtable/itable information to perform method selection on each linked subclass
|
||||
// and ignores all non yet linked ones (speculatively treat them as "effectively abstract").
|
||||
class LinkedConcreteMethodFinder : public AbstractClassHierarchyWalker {
|
||||
private:
|
||||
InstanceKlass* _resolved_klass; // resolved class (JVMS-5.4.3.1)
|
||||
InstanceKlass* _declaring_klass; // the holder of resolved method (JVMS-5.4.3.3)
|
||||
int _vtable_index; // vtable/itable index of the resolved method
|
||||
bool _do_itable_lookup; // choose between itable and vtable lookup logic
|
||||
|
||||
// cache of method lookups
|
||||
Method* _found_methods[PARTICIPANT_LIMIT+1];
|
||||
|
||||
bool is_witness(Klass* k);
|
||||
Method* select_method(InstanceKlass* recv_klass);
|
||||
static int compute_vtable_index(InstanceKlass* resolved_klass, Method* resolved_method, bool& is_itable_index);
|
||||
static bool is_concrete_klass(InstanceKlass* ik);
|
||||
|
||||
void add_participant(Method* m, Klass* participant) {
|
||||
uint np = num_participants();
|
||||
AbstractClassHierarchyWalker::add_participant(participant);
|
||||
assert(np + 1 == num_participants(), "sanity");
|
||||
_found_methods[np] = m; // record the method for the participant
|
||||
}
|
||||
|
||||
bool record_witness(Klass* witness, Method* m) {
|
||||
for (uint i = 0; i < num_participants(); i++) {
|
||||
if (found_method(i) == m) {
|
||||
return false; // already recorded
|
||||
}
|
||||
}
|
||||
// Record not yet seen method.
|
||||
_found_methods[num_participants()] = m;
|
||||
return AbstractClassHierarchyWalker::record_witness(witness);
|
||||
}
|
||||
|
||||
void initialize(Method* participant) {
|
||||
for (uint i = 0; i < PARTICIPANT_LIMIT+1; i++) {
|
||||
_found_methods[i] = NULL;
|
||||
}
|
||||
if (participant != NULL) {
|
||||
add_participant(participant, participant->method_holder());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual Klass* find_witness_in(KlassDepChange& changes);
|
||||
virtual Klass* find_witness_anywhere(InstanceKlass* context_type);
|
||||
|
||||
public:
|
||||
// In order to perform method selection, the following info is needed:
|
||||
// (1) interface or virtual call;
|
||||
// (2) vtable/itable index;
|
||||
// (3) declaring class (in case of interface call).
|
||||
//
|
||||
// It is prepared based on the results of method resolution: resolved class and resolved method (as specified in JVMS-5.4.3.3).
|
||||
// Optionally, a method which was previously determined as a unique target (uniqm) is added as a participant
|
||||
// to enable dependency spot-checking and speed up the search.
|
||||
LinkedConcreteMethodFinder(InstanceKlass* resolved_klass, Method* resolved_method, Method* uniqm = NULL) : AbstractClassHierarchyWalker(NULL) {
|
||||
assert(UseVtableBasedCHA, "required");
|
||||
assert(resolved_klass->is_linked(), "required");
|
||||
assert(resolved_method->method_holder()->is_linked(), "required");
|
||||
assert(!resolved_method->can_be_statically_bound(), "no vtable index available");
|
||||
|
||||
_resolved_klass = resolved_klass;
|
||||
_declaring_klass = resolved_method->method_holder();
|
||||
_vtable_index = compute_vtable_index(resolved_klass, resolved_method,
|
||||
_do_itable_lookup); // out parameter
|
||||
assert(_vtable_index >= 0, "invalid vtable index");
|
||||
|
||||
initialize(uniqm);
|
||||
}
|
||||
|
||||
// Note: If n==num_participants, returns NULL.
|
||||
Method* found_method(uint n) {
|
||||
assert(n <= num_participants(), "oob");
|
||||
assert(participant(n) != NULL || n == num_participants(), "proper usage");
|
||||
return _found_methods[n];
|
||||
}
|
||||
};
|
||||
|
||||
Klass* LinkedConcreteMethodFinder::find_witness_in(KlassDepChange& changes) {
|
||||
Klass* type = changes.type();
|
||||
|
||||
assert(!is_participant(type), "only old classes are participants");
|
||||
|
||||
if (is_witness(type)) {
|
||||
return type;
|
||||
}
|
||||
return NULL; // No witness found. The dependency remains unbroken.
|
||||
}
|
||||
|
||||
Klass* LinkedConcreteMethodFinder::find_witness_anywhere(InstanceKlass* context_type) {
|
||||
for (CountingClassHierarchyIterator iter(context_type); !iter.done(); iter.next()) {
|
||||
Klass* sub = iter.klass();
|
||||
if (is_witness(sub)) {
|
||||
return sub;
|
||||
}
|
||||
if (sub->is_instance_klass() && !InstanceKlass::cast(sub)->is_linked()) {
|
||||
iter.skip_subclasses(); // ignore not yet linked classes
|
||||
}
|
||||
}
|
||||
return NULL; // No witness found. The dependency remains unbroken.
|
||||
}
|
||||
|
||||
bool LinkedConcreteMethodFinder::is_witness(Klass* k) {
|
||||
if (is_participant(k)) {
|
||||
return false; // do not report participant types
|
||||
} else if (k->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
if (is_concrete_klass(ik)) {
|
||||
Method* m = select_method(ik);
|
||||
return record_witness(ik, m);
|
||||
} else {
|
||||
return false; // ignore non-concrete holder class
|
||||
}
|
||||
} else {
|
||||
return false; // no methods to find in an array type
|
||||
}
|
||||
}
|
||||
|
||||
Method* LinkedConcreteMethodFinder::select_method(InstanceKlass* recv_klass) {
|
||||
Method* selected_method = NULL;
|
||||
if (_do_itable_lookup) {
|
||||
assert(_declaring_klass->is_interface(), "sanity");
|
||||
bool implements_interface; // initialized by method_at_itable_or_null()
|
||||
selected_method = recv_klass->method_at_itable_or_null(_declaring_klass, _vtable_index,
|
||||
implements_interface); // out parameter
|
||||
assert(implements_interface, "not implemented");
|
||||
} else {
|
||||
selected_method = recv_klass->method_at_vtable(_vtable_index);
|
||||
}
|
||||
return selected_method; // NULL when corresponding slot is empty (AbstractMethodError case)
|
||||
}
|
||||
|
||||
int LinkedConcreteMethodFinder::compute_vtable_index(InstanceKlass* resolved_klass, Method* resolved_method,
|
||||
// out parameter
|
||||
bool& is_itable_index) {
|
||||
if (resolved_klass->is_interface() && resolved_method->has_itable_index()) {
|
||||
is_itable_index = true;
|
||||
return resolved_method->itable_index();
|
||||
}
|
||||
// Check for default or miranda method first.
|
||||
InstanceKlass* declaring_klass = resolved_method->method_holder();
|
||||
if (!resolved_klass->is_interface() && declaring_klass->is_interface()) {
|
||||
is_itable_index = false;
|
||||
return resolved_klass->vtable_index_of_interface_method(resolved_method);
|
||||
}
|
||||
// At this point we are sure that resolved_method is virtual and not
|
||||
// a default or miranda method; therefore, it must have a valid vtable index.
|
||||
assert(resolved_method->has_vtable_index(), "");
|
||||
is_itable_index = false;
|
||||
return resolved_method->vtable_index();
|
||||
}
|
||||
|
||||
bool LinkedConcreteMethodFinder::is_concrete_klass(InstanceKlass* ik) {
|
||||
if (!Dependencies::is_concrete_klass(ik)) {
|
||||
return false; // not concrete
|
||||
}
|
||||
if (ik->is_interface()) {
|
||||
return false; // interfaces aren't concrete
|
||||
}
|
||||
if (!ik->is_linked()) {
|
||||
return false; // not yet linked classes don't have instances
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// Assert that m is inherited into ctxk, without intervening overrides.
|
||||
// (May return true even if this is not true, in corner cases where we punt.)
|
||||
@ -1501,7 +1717,7 @@ Klass* Dependencies::check_leaf_type(InstanceKlass* ctxk) {
|
||||
// when dealing with the types of actual instances.
|
||||
Klass* Dependencies::check_abstract_with_unique_concrete_subtype(InstanceKlass* ctxk,
|
||||
Klass* conck,
|
||||
KlassDepChange* changes) {
|
||||
NewKlassDepChange* changes) {
|
||||
ConcreteSubtypeFinder wf(conck);
|
||||
Klass* k = wf.find_witness(ctxk, changes);
|
||||
return k;
|
||||
@ -1538,12 +1754,11 @@ Klass* Dependencies::find_unique_concrete_subtype(InstanceKlass* ctxk) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If a class (or interface) has a unique concrete method uniqm, return NULL.
|
||||
// Otherwise, return a class that contains an interfering method.
|
||||
Klass* Dependencies::check_unique_concrete_method(InstanceKlass* ctxk,
|
||||
Method* uniqm,
|
||||
KlassDepChange* changes) {
|
||||
NewKlassDepChange* changes) {
|
||||
// Here is a missing optimization: If uniqm->is_final(),
|
||||
// we don't really need to search beneath it for overrides.
|
||||
// This is probably not important, since we don't use dependencies
|
||||
@ -1557,7 +1772,7 @@ Klass* Dependencies::check_unique_concrete_method(InstanceKlass* ctxk,
|
||||
// (The method m must be defined or inherited in ctxk.)
|
||||
// Include m itself in the set, unless it is abstract.
|
||||
// If this set has exactly one element, return that element.
|
||||
Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk, Method* m) {
|
||||
Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk, Method* m, Klass** participant) {
|
||||
// Return NULL if m is marked old; must have been a redefined method.
|
||||
if (m->is_old()) {
|
||||
return NULL;
|
||||
@ -1568,6 +1783,9 @@ Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk, Method* m
|
||||
Klass* wit = wf.find_witness(ctxk);
|
||||
if (wit != NULL) return NULL; // Too many witnesses.
|
||||
Method* fm = wf.found_method(0); // Will be NULL if num_parts == 0.
|
||||
if (participant != NULL) {
|
||||
(*participant) = wf.participant(0);
|
||||
}
|
||||
if (Dependencies::is_concrete_method(m, ctxk)) {
|
||||
if (fm == NULL) {
|
||||
// It turns out that m was always the only implementation.
|
||||
@ -1588,7 +1806,95 @@ Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk, Method* m
|
||||
return fm;
|
||||
}
|
||||
|
||||
Klass* Dependencies::check_has_no_finalizable_subclasses(InstanceKlass* ctxk, KlassDepChange* changes) {
|
||||
// If a class (or interface) has a unique concrete method uniqm, return NULL.
|
||||
// Otherwise, return a class that contains an interfering method.
|
||||
Klass* Dependencies::check_unique_concrete_method(InstanceKlass* ctxk,
|
||||
Method* uniqm,
|
||||
Klass* resolved_klass,
|
||||
Method* resolved_method,
|
||||
KlassDepChange* changes) {
|
||||
assert(UseVtableBasedCHA, "required");
|
||||
assert(!ctxk->is_interface() || ctxk == resolved_klass, "sanity");
|
||||
assert(!resolved_method->can_be_statically_bound() || resolved_method == uniqm, "sanity");
|
||||
assert(resolved_klass->is_subtype_of(resolved_method->method_holder()), "sanity");
|
||||
|
||||
if (!InstanceKlass::cast(resolved_klass)->is_linked() ||
|
||||
!resolved_method->method_holder()->is_linked() ||
|
||||
resolved_method->can_be_statically_bound()) {
|
||||
// Dependency is redundant, but benign. Just keep it to avoid unnecessary recompilation.
|
||||
return NULL; // no vtable index available
|
||||
}
|
||||
|
||||
LinkedConcreteMethodFinder mf(InstanceKlass::cast(resolved_klass), resolved_method, uniqm);
|
||||
return mf.find_witness(ctxk, changes);
|
||||
}
|
||||
|
||||
// Find the set of all non-abstract methods under ctxk that match m.
|
||||
// (The method m must be defined or inherited in ctxk.)
|
||||
// Include m itself in the set, unless it is abstract.
|
||||
// If this set has exactly one element, return that element.
|
||||
// Not yet linked subclasses of ctxk are ignored since they don't have any instances yet.
|
||||
// Additionally, resolved_klass and resolved_method complete the description of the call site being analyzed.
|
||||
Method* Dependencies::find_unique_concrete_method(InstanceKlass* ctxk, Method* m, Klass* resolved_klass, Method* resolved_method) {
|
||||
// Return NULL if m is marked old; must have been a redefined method.
|
||||
if (m->is_old()) {
|
||||
return NULL;
|
||||
}
|
||||
if (!InstanceKlass::cast(resolved_klass)->is_linked() ||
|
||||
!resolved_method->method_holder()->is_linked() ||
|
||||
resolved_method->can_be_statically_bound()) {
|
||||
return m; // nothing to do: no witness under ctxk
|
||||
}
|
||||
LinkedConcreteMethodFinder wf(InstanceKlass::cast(resolved_klass), resolved_method);
|
||||
assert(Dependencies::verify_method_context(ctxk, m), "proper context");
|
||||
wf.record_witnesses(1);
|
||||
Klass* wit = wf.find_witness(ctxk);
|
||||
if (wit != NULL) {
|
||||
return NULL; // Too many witnesses.
|
||||
}
|
||||
// p == NULL when no participants are found (wf.num_participants() == 0).
|
||||
// fm == NULL case has 2 meanings:
|
||||
// * when p == NULL: no method found;
|
||||
// * when p != NULL: AbstractMethodError-throwing method found.
|
||||
// Also, found method should always be accompanied by a participant class.
|
||||
Klass* p = wf.participant(0);
|
||||
Method* fm = wf.found_method(0);
|
||||
assert(fm == NULL || p != NULL, "no participant");
|
||||
// Normalize all error-throwing cases to NULL.
|
||||
if (fm == Universe::throw_illegal_access_error() ||
|
||||
fm == Universe::throw_no_such_method_error() ||
|
||||
!Dependencies::is_concrete_method(fm, p)) {
|
||||
fm = NULL; // error-throwing method
|
||||
}
|
||||
if (Dependencies::is_concrete_method(m, ctxk)) {
|
||||
if (p == NULL) {
|
||||
// It turns out that m was always the only implementation.
|
||||
assert(fm == NULL, "sanity");
|
||||
fm = m;
|
||||
}
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
// Make sure the dependency mechanism will pass this discovery:
|
||||
if (VerifyDependencies && fm != NULL) {
|
||||
guarantee(NULL == check_unique_concrete_method(ctxk, fm, resolved_klass, resolved_method),
|
||||
"verify dep.");
|
||||
}
|
||||
#endif // PRODUCT
|
||||
assert(fm == NULL || !fm->is_abstract(), "sanity");
|
||||
// Old CHA conservatively reports concrete methods in abstract classes
|
||||
// irrespective of whether they have concrete subclasses or not.
|
||||
#ifdef ASSERT
|
||||
Klass* uniqp = NULL;
|
||||
Method* uniqm = Dependencies::find_unique_concrete_method(ctxk, m, &uniqp);
|
||||
assert(uniqm == NULL || uniqm == fm ||
|
||||
uniqm->method_holder()->is_abstract() ||
|
||||
(fm == NULL && uniqm != NULL && uniqp != NULL && !InstanceKlass::cast(uniqp)->is_linked()),
|
||||
"sanity");
|
||||
#endif // ASSERT
|
||||
return fm;
|
||||
}
|
||||
|
||||
Klass* Dependencies::check_has_no_finalizable_subclasses(InstanceKlass* ctxk, NewKlassDepChange* changes) {
|
||||
Klass* search_at = ctxk;
|
||||
if (changes != NULL)
|
||||
search_at = changes->new_type(); // just look at the new bit
|
||||
@ -1624,8 +1930,7 @@ void Dependencies::DepStream::trace_and_log_witness(Klass* witness) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Klass* Dependencies::DepStream::check_klass_dependency(KlassDepChange* changes) {
|
||||
Klass* Dependencies::DepStream::check_new_klass_dependency(NewKlassDepChange* changes) {
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
Dependencies::check_valid_dependency_type(type());
|
||||
|
||||
@ -1640,9 +1945,12 @@ Klass* Dependencies::DepStream::check_klass_dependency(KlassDepChange* changes)
|
||||
case abstract_with_unique_concrete_subtype:
|
||||
witness = check_abstract_with_unique_concrete_subtype(context_type(), type_argument(1), changes);
|
||||
break;
|
||||
case unique_concrete_method:
|
||||
case unique_concrete_method_2:
|
||||
witness = check_unique_concrete_method(context_type(), method_argument(1), changes);
|
||||
break;
|
||||
case unique_concrete_method_4:
|
||||
witness = check_unique_concrete_method(context_type(), method_argument(1), type_argument(2), method_argument(3), changes);
|
||||
break;
|
||||
case no_finalizable_subclasses:
|
||||
witness = check_has_no_finalizable_subclasses(context_type(), changes);
|
||||
break;
|
||||
@ -1654,6 +1962,41 @@ Klass* Dependencies::DepStream::check_klass_dependency(KlassDepChange* changes)
|
||||
return witness;
|
||||
}
|
||||
|
||||
Klass* Dependencies::DepStream::check_klass_init_dependency(KlassInitDepChange* changes) {
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
Dependencies::check_valid_dependency_type(type());
|
||||
|
||||
// No new types added. Only unique_concrete_method_4 is sensitive to class initialization changes.
|
||||
Klass* witness = NULL;
|
||||
switch (type()) {
|
||||
case unique_concrete_method_4:
|
||||
witness = check_unique_concrete_method(context_type(), method_argument(1), type_argument(2), method_argument(3), changes);
|
||||
break;
|
||||
default:
|
||||
witness = NULL;
|
||||
break;
|
||||
}
|
||||
trace_and_log_witness(witness);
|
||||
return witness;
|
||||
}
|
||||
|
||||
Klass* Dependencies::DepStream::check_klass_dependency(KlassDepChange* changes) {
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
Dependencies::check_valid_dependency_type(type());
|
||||
|
||||
if (changes != NULL) {
|
||||
if (UseVtableBasedCHA && changes->is_klass_init_change()) {
|
||||
return check_klass_init_dependency(changes->as_klass_init_change());
|
||||
} else {
|
||||
return check_new_klass_dependency(changes->as_new_klass_change());
|
||||
}
|
||||
} else {
|
||||
Klass* witness = check_new_klass_dependency(NULL);
|
||||
// check_klass_init_dependency duplicates check_new_klass_dependency checks when class hierarchy change info is absent.
|
||||
assert(witness != NULL || check_klass_init_dependency(NULL) == NULL, "missed dependency");
|
||||
return witness;
|
||||
}
|
||||
}
|
||||
|
||||
Klass* Dependencies::DepStream::check_call_site_dependency(CallSiteDepChange* changes) {
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
@ -1719,9 +2062,9 @@ void DepChange::print() {
|
||||
}
|
||||
|
||||
void DepChange::ContextStream::start() {
|
||||
Klass* new_type = _changes.is_klass_change() ? _changes.as_klass_change()->new_type() : (Klass*) NULL;
|
||||
_change_type = (new_type == NULL ? NO_CHANGE : Start_Klass);
|
||||
_klass = new_type;
|
||||
Klass* type = (_changes.is_klass_change() ? _changes.as_klass_change()->type() : (Klass*) NULL);
|
||||
_change_type = (type == NULL ? NO_CHANGE : Start_Klass);
|
||||
_klass = type;
|
||||
_ti_base = NULL;
|
||||
_ti_index = 0;
|
||||
_ti_limit = 0;
|
||||
@ -1791,7 +2134,7 @@ bool KlassDepChange::involves_context(Klass* k) {
|
||||
}
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
bool is_contained = ik->is_marked_dependent();
|
||||
assert(is_contained == new_type()->is_subtype_of(k),
|
||||
assert(is_contained == type()->is_subtype_of(k),
|
||||
"correct marking of potential context types");
|
||||
return is_contained;
|
||||
}
|
||||
|
||||
@ -60,6 +60,8 @@ class CompileLog;
|
||||
class CompileTask;
|
||||
class DepChange;
|
||||
class KlassDepChange;
|
||||
class NewKlassDepChange;
|
||||
class KlassInitDepChange;
|
||||
class CallSiteDepChange;
|
||||
class NoSafepointVerifier;
|
||||
|
||||
@ -132,7 +134,14 @@ class Dependencies: public ResourceObj {
|
||||
// context class CX. M1 must be either inherited in CX or defined
|
||||
// in a subtype* of CX. It asserts that MM(CX, M1) is no greater
|
||||
// than {M1}.
|
||||
unique_concrete_method, // one unique concrete method under CX
|
||||
unique_concrete_method_2, // one unique concrete method under CX
|
||||
|
||||
// In addition to the method M1 and the context class CX, the parameters
|
||||
// to this dependency are the resolved class RC1 and the
|
||||
// resolved method RM1. It asserts that MM(CX, M1, RC1, RM1)
|
||||
// is no greater than {M1}. RC1 and RM1 are used to improve the precision
|
||||
// of the analysis.
|
||||
unique_concrete_method_4, // one unique concrete method under CX
|
||||
|
||||
// This dependency asserts that no instances of class or it's
|
||||
// subclasses require finalization registration.
|
||||
@ -156,7 +165,7 @@ class Dependencies: public ResourceObj {
|
||||
implicit_ctxk_types = 0,
|
||||
explicit_ctxk_types = all_types & ~(non_ctxk_types | implicit_ctxk_types),
|
||||
|
||||
max_arg_count = 3, // current maximum number of arguments (incl. ctxk)
|
||||
max_arg_count = 4, // current maximum number of arguments (incl. ctxk)
|
||||
|
||||
// A "context type" is a class or interface that
|
||||
// provides context for evaluating a dependency.
|
||||
@ -325,6 +334,7 @@ class Dependencies: public ResourceObj {
|
||||
|
||||
void assert_common_1(DepType dept, ciBaseObject* x);
|
||||
void assert_common_2(DepType dept, ciBaseObject* x0, ciBaseObject* x1);
|
||||
void assert_common_4(DepType dept, ciKlass* ctxk, ciBaseObject* x1, ciBaseObject* x2, ciBaseObject* x3);
|
||||
|
||||
public:
|
||||
// Adding assertions to a new dependency set at compile time:
|
||||
@ -332,6 +342,7 @@ class Dependencies: public ResourceObj {
|
||||
void assert_leaf_type(ciKlass* ctxk);
|
||||
void assert_abstract_with_unique_concrete_subtype(ciKlass* ctxk, ciKlass* conck);
|
||||
void assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm);
|
||||
void assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm, ciKlass* resolved_klass, ciMethod* resolved_method);
|
||||
void assert_has_no_finalizable_subclasses(ciKlass* ctxk);
|
||||
void assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle);
|
||||
|
||||
@ -398,9 +409,10 @@ class Dependencies: public ResourceObj {
|
||||
// Checking old assertions at run-time (in the VM only):
|
||||
static Klass* check_evol_method(Method* m);
|
||||
static Klass* check_leaf_type(InstanceKlass* ctxk);
|
||||
static Klass* check_abstract_with_unique_concrete_subtype(InstanceKlass* ctxk, Klass* conck, KlassDepChange* changes = NULL);
|
||||
static Klass* check_unique_concrete_method(InstanceKlass* ctxk, Method* uniqm, KlassDepChange* changes = NULL);
|
||||
static Klass* check_has_no_finalizable_subclasses(InstanceKlass* ctxk, KlassDepChange* changes = NULL);
|
||||
static Klass* check_abstract_with_unique_concrete_subtype(InstanceKlass* ctxk, Klass* conck, NewKlassDepChange* changes = NULL);
|
||||
static Klass* check_unique_concrete_method(InstanceKlass* ctxk, Method* uniqm, NewKlassDepChange* changes = NULL);
|
||||
static Klass* check_unique_concrete_method(InstanceKlass* ctxk, Method* uniqm, Klass* resolved_klass, Method* resolved_method, KlassDepChange* changes = NULL);
|
||||
static Klass* check_has_no_finalizable_subclasses(InstanceKlass* ctxk, NewKlassDepChange* changes = NULL);
|
||||
static Klass* check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
|
||||
// A returned Klass* is NULL if the dependency assertion is still
|
||||
// valid. A non-NULL Klass* is a 'witness' to the assertion
|
||||
@ -418,7 +430,9 @@ class Dependencies: public ResourceObj {
|
||||
|
||||
// Detecting possible new assertions:
|
||||
static Klass* find_unique_concrete_subtype(InstanceKlass* ctxk);
|
||||
static Method* find_unique_concrete_method(InstanceKlass* ctxk, Method* m);
|
||||
static Method* find_unique_concrete_method(InstanceKlass* ctxk, Method* m,
|
||||
Klass** participant = NULL); // out parameter
|
||||
static Method* find_unique_concrete_method(InstanceKlass* ctxk, Method* m, Klass* resolved_klass, Method* resolved_method);
|
||||
|
||||
#ifdef ASSERT
|
||||
static bool verify_method_context(InstanceKlass* ctxk, Method* m);
|
||||
@ -456,7 +470,8 @@ class Dependencies: public ResourceObj {
|
||||
void log_dependency(DepType dept,
|
||||
ciBaseObject* x0,
|
||||
ciBaseObject* x1 = NULL,
|
||||
ciBaseObject* x2 = NULL) {
|
||||
ciBaseObject* x2 = NULL,
|
||||
ciBaseObject* x3 = NULL) {
|
||||
if (log() == NULL) {
|
||||
return;
|
||||
}
|
||||
@ -472,6 +487,9 @@ class Dependencies: public ResourceObj {
|
||||
if (x2 != NULL) {
|
||||
ciargs->push(x2);
|
||||
}
|
||||
if (x3 != NULL) {
|
||||
ciargs->push(x3);
|
||||
}
|
||||
assert(ciargs->length() == dep_args(dept), "");
|
||||
log_dependency(dept, ciargs);
|
||||
}
|
||||
@ -549,6 +567,8 @@ class Dependencies: public ResourceObj {
|
||||
inline oop recorded_oop_at(int i);
|
||||
|
||||
Klass* check_klass_dependency(KlassDepChange* changes);
|
||||
Klass* check_new_klass_dependency(NewKlassDepChange* changes);
|
||||
Klass* check_klass_init_dependency(KlassInitDepChange* changes);
|
||||
Klass* check_call_site_dependency(CallSiteDepChange* changes);
|
||||
|
||||
void trace_and_log_witness(Klass* witness);
|
||||
@ -647,8 +667,10 @@ class DependencySignature : public ResourceObj {
|
||||
class DepChange : public StackObj {
|
||||
public:
|
||||
// What kind of DepChange is this?
|
||||
virtual bool is_klass_change() const { return false; }
|
||||
virtual bool is_call_site_change() const { return false; }
|
||||
virtual bool is_klass_change() const { return false; }
|
||||
virtual bool is_new_klass_change() const { return false; }
|
||||
virtual bool is_klass_init_change() const { return false; }
|
||||
virtual bool is_call_site_change() const { return false; }
|
||||
|
||||
virtual void mark_for_deoptimization(nmethod* nm) = 0;
|
||||
|
||||
@ -657,6 +679,14 @@ class DepChange : public StackObj {
|
||||
assert(is_klass_change(), "bad cast");
|
||||
return (KlassDepChange*) this;
|
||||
}
|
||||
NewKlassDepChange* as_new_klass_change() {
|
||||
assert(is_new_klass_change(), "bad cast");
|
||||
return (NewKlassDepChange*) this;
|
||||
}
|
||||
KlassInitDepChange* as_klass_init_change() {
|
||||
assert(is_klass_init_change(), "bad cast");
|
||||
return (KlassInitDepChange*) this;
|
||||
}
|
||||
CallSiteDepChange* as_call_site_change() {
|
||||
assert(is_call_site_change(), "bad cast");
|
||||
return (CallSiteDepChange*) this;
|
||||
@ -716,28 +746,27 @@ class DepChange : public StackObj {
|
||||
|
||||
|
||||
// A class hierarchy change coming through the VM (under the Compile_lock).
|
||||
// The change is structured as a single new type with any number of supers
|
||||
// and implemented interface types. Other than the new type, any of the
|
||||
// The change is structured as a single type with any number of supers
|
||||
// and implemented interface types. Other than the type, any of the
|
||||
// super types can be context types for a relevant dependency, which the
|
||||
// new type could invalidate.
|
||||
// type could invalidate.
|
||||
class KlassDepChange : public DepChange {
|
||||
private:
|
||||
// each change set is rooted in exactly one new type (at present):
|
||||
InstanceKlass* _new_type;
|
||||
// each change set is rooted in exactly one type (at present):
|
||||
InstanceKlass* _type;
|
||||
|
||||
void initialize();
|
||||
|
||||
public:
|
||||
// notes the new type, marks it and all its super-types
|
||||
KlassDepChange(InstanceKlass* new_type)
|
||||
: _new_type(new_type)
|
||||
{
|
||||
protected:
|
||||
// notes the type, marks it and all its super-types
|
||||
KlassDepChange(InstanceKlass* type) : _type(type) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
// cleans up the marks
|
||||
~KlassDepChange();
|
||||
|
||||
public:
|
||||
// What kind of DepChange is this?
|
||||
virtual bool is_klass_change() const { return true; }
|
||||
|
||||
@ -745,12 +774,31 @@ class KlassDepChange : public DepChange {
|
||||
nm->mark_for_deoptimization(/*inc_recompile_counts=*/true);
|
||||
}
|
||||
|
||||
InstanceKlass* new_type() { return _new_type; }
|
||||
InstanceKlass* type() { return _type; }
|
||||
|
||||
// involves_context(k) is true if k is new_type or any of the super types
|
||||
// involves_context(k) is true if k == _type or any of its super types
|
||||
bool involves_context(Klass* k);
|
||||
};
|
||||
|
||||
// A class hierarchy change: new type is loaded.
|
||||
class NewKlassDepChange : public KlassDepChange {
|
||||
public:
|
||||
NewKlassDepChange(InstanceKlass* new_type) : KlassDepChange(new_type) {}
|
||||
|
||||
// What kind of DepChange is this?
|
||||
virtual bool is_new_klass_change() const { return true; }
|
||||
|
||||
InstanceKlass* new_type() { return type(); }
|
||||
};
|
||||
|
||||
// Change in initialization state of a loaded class.
|
||||
class KlassInitDepChange : public KlassDepChange {
|
||||
public:
|
||||
KlassInitDepChange(InstanceKlass* type) : KlassDepChange(type) {}
|
||||
|
||||
// What kind of DepChange is this?
|
||||
virtual bool is_klass_init_change() const { return true; }
|
||||
};
|
||||
|
||||
// A CallSite has changed its target.
|
||||
class CallSiteDepChange : public DepChange {
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
#include "classfile/verifier.hpp"
|
||||
#include "classfile/vmClasses.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "code/dependencyContext.hpp"
|
||||
#include "compiler/compilationPolicy.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
@ -955,7 +956,17 @@ bool InstanceKlass::link_class_impl(TRAPS) {
|
||||
// In case itable verification is ever added.
|
||||
// itable().verify(tty, true);
|
||||
#endif
|
||||
set_init_state(linked);
|
||||
if (UseVtableBasedCHA) {
|
||||
MutexLocker ml(THREAD, Compile_lock);
|
||||
set_init_state(linked);
|
||||
|
||||
// Now flush all code that assume the class is not linked.
|
||||
if (Universe::is_fully_initialized()) {
|
||||
CodeCache::flush_dependents_on(this);
|
||||
}
|
||||
} else {
|
||||
set_init_state(linked);
|
||||
}
|
||||
if (JvmtiExport::should_post_class_prepare()) {
|
||||
JvmtiExport::post_class_prepare(THREAD->as_Java_thread(), this);
|
||||
}
|
||||
|
||||
@ -877,7 +877,7 @@ class Compile : public Phase {
|
||||
const TypeOopPtr* receiver_type, bool is_virtual,
|
||||
bool &call_does_dispatch, int &vtable_index,
|
||||
bool check_access = true);
|
||||
ciMethod* optimize_inlining(ciMethod* caller, ciInstanceKlass* klass,
|
||||
ciMethod* optimize_inlining(ciMethod* caller, ciInstanceKlass* klass, ciKlass* holder,
|
||||
ciMethod* callee, const TypeOopPtr* receiver_type,
|
||||
bool check_access = true);
|
||||
|
||||
|
||||
@ -324,7 +324,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
|
||||
|
||||
CallGenerator* cg = CallGenerator::for_guarded_call(holder, miss_cg, hit_cg);
|
||||
if (hit_cg != NULL && cg != NULL) {
|
||||
dependencies()->assert_unique_concrete_method(declared_interface, cha_monomorphic_target);
|
||||
dependencies()->assert_unique_concrete_method(declared_interface, cha_monomorphic_target, declared_interface, callee);
|
||||
return cg;
|
||||
}
|
||||
}
|
||||
@ -1071,7 +1071,7 @@ ciMethod* Compile::optimize_virtual_call(ciMethod* caller, ciInstanceKlass* klas
|
||||
vtable_index = Method::invalid_vtable_index;
|
||||
|
||||
// Choose call strategy.
|
||||
ciMethod* optimized_virtual_method = optimize_inlining(caller, klass, callee,
|
||||
ciMethod* optimized_virtual_method = optimize_inlining(caller, klass, holder, callee,
|
||||
receiver_type, check_access);
|
||||
|
||||
// Have the call been sufficiently improved such that it is no longer a virtual?
|
||||
@ -1086,7 +1086,7 @@ ciMethod* Compile::optimize_virtual_call(ciMethod* caller, ciInstanceKlass* klas
|
||||
}
|
||||
|
||||
// Identify possible target method and inlining style
|
||||
ciMethod* Compile::optimize_inlining(ciMethod* caller, ciInstanceKlass* klass,
|
||||
ciMethod* Compile::optimize_inlining(ciMethod* caller, ciInstanceKlass* klass, ciKlass* holder,
|
||||
ciMethod* callee, const TypeOopPtr* receiver_type,
|
||||
bool check_access) {
|
||||
// only use for virtual or interface calls
|
||||
@ -1165,7 +1165,7 @@ ciMethod* Compile::optimize_inlining(ciMethod* caller, ciInstanceKlass* klass,
|
||||
// by dynamic class loading. Be sure to test the "static" receiver
|
||||
// dest_method here, as opposed to the actual receiver, which may
|
||||
// falsely lead us to believe that the receiver is final or private.
|
||||
dependencies()->assert_unique_concrete_method(actual_receiver, cha_monomorphic_target);
|
||||
dependencies()->assert_unique_concrete_method(actual_receiver, cha_monomorphic_target, holder, callee);
|
||||
}
|
||||
return cha_monomorphic_target;
|
||||
}
|
||||
|
||||
@ -994,6 +994,9 @@ const intx ObjectAlignmentInBytes = 8;
|
||||
develop(bool, UseCHA, true, \
|
||||
"Enable CHA") \
|
||||
\
|
||||
product(bool, UseVtableBasedCHA, true, DIAGNOSTIC, \
|
||||
"Use vtable information during CHA") \
|
||||
\
|
||||
product(bool, UseTypeProfile, true, \
|
||||
"Check interpreter profile for historically monomorphic calls") \
|
||||
\
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @requires !vm.graal.enabled
|
||||
* @requires !vm.graal.enabled & vm.opt.final.UseVtableBasedCHA == true
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* java.base/jdk.internal.misc
|
||||
* java.base/jdk.internal.vm.annotation
|
||||
@ -263,7 +263,8 @@ public class StrengthReduceInterfaceCall {
|
||||
|
||||
interface K1 extends I {}
|
||||
interface K2 extends I { Object m(); }
|
||||
interface K3 extends I { default Object m() { return WRONG; }}
|
||||
interface K3 extends I { default Object m() { return WRONG; }}
|
||||
interface K4 extends I { default Object m() { return CORRECT; }}
|
||||
|
||||
static class D implements I { public Object m() { return WRONG; }}
|
||||
|
||||
@ -318,19 +319,43 @@ public class StrengthReduceInterfaceCall {
|
||||
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
|
||||
// 1. Dependency invalidation
|
||||
// 1. No invalidation: interfaces don't participate in CHA.
|
||||
initialize(K3.class); // intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
||||
assertNotCompiled();
|
||||
assertCompiled();
|
||||
|
||||
// 2. Recompilation: still inlines
|
||||
// FIXME: no default method support in CHA yet
|
||||
compile(megamorphic());
|
||||
// 2. Dependency invalidation on K3n <: I with concrete method.
|
||||
call(new K3() { public Object m() { return CORRECT; }}); // K3n.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m ABSTRACT
|
||||
assertNotCompiled();
|
||||
|
||||
// 3. Recompilation: no inlining, no dependencies
|
||||
compile(megamorphic());
|
||||
call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
||||
call(new K3() { public Object m() { return CORRECT; }}); // K3n.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
||||
assertCompiled();
|
||||
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
}
|
||||
|
||||
@TestCase
|
||||
public void testMega3() {
|
||||
// 0. Trigger compilation of a megamorphic call site
|
||||
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
||||
assertCompiled();
|
||||
|
||||
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
||||
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
|
||||
// 1. No invalidation: interfaces don't participate in CHA.
|
||||
initialize(K4.class); // intf K4.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
||||
assertCompiled();
|
||||
|
||||
// 2. Dependency invalidation on K4n <: I with default method.
|
||||
call(new K4() { /* default method K4.m */ }); // K4n <: intf K4.m DEFAULT <: intf I.m ABSTRACT <: intf J.m ABSTRACT
|
||||
assertNotCompiled();
|
||||
|
||||
// 3. Recompilation: no inlining, no dependencies
|
||||
compile(megamorphic());
|
||||
call(new K4() { /* default method K4.m */ }); // K4n <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
||||
assertCompiled();
|
||||
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
@ -360,7 +385,8 @@ public class StrengthReduceInterfaceCall {
|
||||
|
||||
interface K1 extends I {}
|
||||
interface K2 extends I { Object m(); }
|
||||
interface K3 extends I { default Object m() { return WRONG; }}
|
||||
interface K3 extends I { default Object m() { return WRONG; }}
|
||||
interface K4 extends I { default Object m() { return CORRECT; }}
|
||||
|
||||
static class C implements I { public Object m() { return CORRECT; }}
|
||||
|
||||
@ -413,12 +439,8 @@ public class StrengthReduceInterfaceCall {
|
||||
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
|
||||
// Dependency invalidation
|
||||
// No invalidation: interfaces don't participate in CHA.
|
||||
initialize(K3.class); // intf K3.m DEFAULT <: intf I;
|
||||
assertNotCompiled(); // FIXME: default methods in sub-interfaces shouldn't be taken into account by CHA
|
||||
|
||||
// Recompilation with a dependency
|
||||
compile(megamorphic());
|
||||
assertCompiled();
|
||||
|
||||
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
||||
@ -436,6 +458,34 @@ public class StrengthReduceInterfaceCall {
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
@TestCase
|
||||
public void testMega3() {
|
||||
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT
|
||||
assertCompiled();
|
||||
|
||||
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
||||
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
|
||||
// No invalidation: interfaces don't participate in CHA.
|
||||
initialize(K4.class); // intf K3.m DEFAULT <: intf I;
|
||||
assertCompiled();
|
||||
|
||||
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
||||
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
|
||||
call(new K4() { /* default method K4.m */ }); // K4n <: K4.m DEFAULT <: intf I <: intf J.m ABSTRACT
|
||||
assertNotCompiled();
|
||||
|
||||
// Recompilation w/o a dependency
|
||||
compile(megamorphic());
|
||||
// Dependency: none
|
||||
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
||||
call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user