mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8057967: CallSite dependency tracking scales devastatingly poorly
Reviewed-by: jrose, roland, plevart, shade
This commit is contained in:
parent
cb7a08139b
commit
f98a23137c
12 changed files with 337 additions and 33 deletions
|
@ -49,6 +49,25 @@ ciMethodHandle* ciCallSite::get_target() const {
|
||||||
return CURRENT_ENV->get_object(method_handle_oop)->as_method_handle();
|
return CURRENT_ENV->get_object(method_handle_oop)->as_method_handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// ciCallSite::get_context
|
||||||
|
//
|
||||||
|
// Return the target MethodHandle of this CallSite.
|
||||||
|
ciKlass* ciCallSite::get_context() {
|
||||||
|
assert(!is_constant_call_site(), "");
|
||||||
|
|
||||||
|
VM_ENTRY_MARK;
|
||||||
|
oop call_site_oop = get_oop();
|
||||||
|
InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site_oop);
|
||||||
|
if (ctxk == NULL) {
|
||||||
|
// The call site doesn't have a context associated. Set it to the default context.
|
||||||
|
oop def_context_oop = java_lang_invoke_CallSite::default_context();
|
||||||
|
java_lang_invoke_CallSite::set_context_cas(call_site_oop, def_context_oop, /*expected=*/NULL);
|
||||||
|
ctxk = MethodHandles::get_call_site_context(call_site_oop);
|
||||||
|
}
|
||||||
|
return (CURRENT_ENV->get_metadata(ctxk))->as_klass();
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// ciCallSite::print
|
// ciCallSite::print
|
||||||
//
|
//
|
||||||
|
|
|
@ -43,6 +43,7 @@ public:
|
||||||
|
|
||||||
// Return the target MethodHandle of this CallSite.
|
// Return the target MethodHandle of this CallSite.
|
||||||
ciMethodHandle* get_target() const;
|
ciMethodHandle* get_target() const;
|
||||||
|
ciKlass* get_context();
|
||||||
|
|
||||||
void print();
|
void print();
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,21 +102,22 @@ InjectedField* JavaClasses::get_injected(Symbol* class_name, int* field_count) {
|
||||||
static bool find_field(InstanceKlass* ik,
|
static bool find_field(InstanceKlass* ik,
|
||||||
Symbol* name_symbol, Symbol* signature_symbol,
|
Symbol* name_symbol, Symbol* signature_symbol,
|
||||||
fieldDescriptor* fd,
|
fieldDescriptor* fd,
|
||||||
bool allow_super = false) {
|
bool is_static = false, bool allow_super = false) {
|
||||||
if (allow_super)
|
if (allow_super || is_static) {
|
||||||
return ik->find_field(name_symbol, signature_symbol, fd) != NULL;
|
return ik->find_field(name_symbol, signature_symbol, is_static, fd) != NULL;
|
||||||
else
|
} else {
|
||||||
return ik->find_local_field(name_symbol, signature_symbol, fd);
|
return ik->find_local_field(name_symbol, signature_symbol, fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpful routine for computing field offsets at run time rather than hardcoding them
|
// Helpful routine for computing field offsets at run time rather than hardcoding them
|
||||||
static void
|
static void
|
||||||
compute_offset(int &dest_offset,
|
compute_offset(int &dest_offset,
|
||||||
Klass* klass_oop, Symbol* name_symbol, Symbol* signature_symbol,
|
Klass* klass_oop, Symbol* name_symbol, Symbol* signature_symbol,
|
||||||
bool allow_super = false) {
|
bool is_static = false, bool allow_super = false) {
|
||||||
fieldDescriptor fd;
|
fieldDescriptor fd;
|
||||||
InstanceKlass* ik = InstanceKlass::cast(klass_oop);
|
InstanceKlass* ik = InstanceKlass::cast(klass_oop);
|
||||||
if (!find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) {
|
if (!find_field(ik, name_symbol, signature_symbol, &fd, is_static, allow_super)) {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
tty->print_cr("Invalid layout of %s at %s", ik->external_name(), name_symbol->as_C_string());
|
tty->print_cr("Invalid layout of %s at %s", ik->external_name(), name_symbol->as_C_string());
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
|
@ -2972,14 +2973,49 @@ int java_lang_invoke_MethodType::rtype_slot_count(oop mt) {
|
||||||
// Support for java_lang_invoke_CallSite
|
// Support for java_lang_invoke_CallSite
|
||||||
|
|
||||||
int java_lang_invoke_CallSite::_target_offset;
|
int java_lang_invoke_CallSite::_target_offset;
|
||||||
|
int java_lang_invoke_CallSite::_context_offset;
|
||||||
|
int java_lang_invoke_CallSite::_default_context_offset;
|
||||||
|
|
||||||
void java_lang_invoke_CallSite::compute_offsets() {
|
void java_lang_invoke_CallSite::compute_offsets() {
|
||||||
Klass* k = SystemDictionary::CallSite_klass();
|
Klass* k = SystemDictionary::CallSite_klass();
|
||||||
if (k != NULL) {
|
if (k != NULL) {
|
||||||
compute_offset(_target_offset, k, vmSymbols::target_name(), vmSymbols::java_lang_invoke_MethodHandle_signature());
|
compute_offset(_target_offset, k, vmSymbols::target_name(), vmSymbols::java_lang_invoke_MethodHandle_signature());
|
||||||
|
compute_offset(_context_offset, k, vmSymbols::context_name(), vmSymbols::sun_misc_Cleaner_signature());
|
||||||
|
compute_offset(_default_context_offset, k,
|
||||||
|
vmSymbols::DEFAULT_CONTEXT_name(), vmSymbols::sun_misc_Cleaner_signature(),
|
||||||
|
/*is_static=*/true, /*allow_super=*/false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oop java_lang_invoke_CallSite::context_volatile(oop call_site) {
|
||||||
|
assert(java_lang_invoke_CallSite::is_instance(call_site), "");
|
||||||
|
|
||||||
|
oop dep_oop = call_site->obj_field_volatile(_context_offset);
|
||||||
|
return dep_oop;
|
||||||
|
}
|
||||||
|
|
||||||
|
void java_lang_invoke_CallSite::set_context_volatile(oop call_site, oop context) {
|
||||||
|
assert(java_lang_invoke_CallSite::is_instance(call_site), "");
|
||||||
|
call_site->obj_field_put_volatile(_context_offset, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool java_lang_invoke_CallSite::set_context_cas(oop call_site, oop context, oop expected) {
|
||||||
|
assert(java_lang_invoke_CallSite::is_instance(call_site), "");
|
||||||
|
HeapWord* context_addr = call_site->obj_field_addr<HeapWord>(_context_offset);
|
||||||
|
oop res = oopDesc::atomic_compare_exchange_oop(context, context_addr, expected, true);
|
||||||
|
bool success = (res == expected);
|
||||||
|
if (success) {
|
||||||
|
update_barrier_set((void*)context_addr, context);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
oop java_lang_invoke_CallSite::default_context() {
|
||||||
|
InstanceKlass* ik = InstanceKlass::cast(SystemDictionary::CallSite_klass());
|
||||||
|
oop def_context_oop = ik->java_mirror()->obj_field(_default_context_offset);
|
||||||
|
assert(!oopDesc::is_null(def_context_oop), "");
|
||||||
|
return def_context_oop;
|
||||||
|
}
|
||||||
|
|
||||||
// Support for java_security_AccessControlContext
|
// Support for java_security_AccessControlContext
|
||||||
|
|
||||||
|
|
|
@ -961,7 +961,6 @@ class java_lang_ref_SoftReference: public java_lang_ref_Reference {
|
||||||
static void set_clock(jlong value);
|
static void set_clock(jlong value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Interface to java.lang.invoke.MethodHandle objects
|
// Interface to java.lang.invoke.MethodHandle objects
|
||||||
|
|
||||||
class MethodHandleEntry;
|
class MethodHandleEntry;
|
||||||
|
@ -1173,16 +1172,25 @@ class java_lang_invoke_CallSite: AllStatic {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int _target_offset;
|
static int _target_offset;
|
||||||
|
static int _context_offset;
|
||||||
|
static int _default_context_offset;
|
||||||
|
|
||||||
|
|
||||||
static void compute_offsets();
|
static void compute_offsets();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Accessors
|
// Accessors
|
||||||
static oop target( oop site);
|
static oop target( oop site);
|
||||||
static void set_target( oop site, oop target);
|
static void set_target( oop site, oop target);
|
||||||
|
|
||||||
static volatile oop target_volatile(oop site);
|
static volatile oop target_volatile( oop site);
|
||||||
static void set_target_volatile(oop site, oop target);
|
static void set_target_volatile( oop site, oop target);
|
||||||
|
|
||||||
|
static oop context_volatile(oop site);
|
||||||
|
static void set_context_volatile(oop site, oop context);
|
||||||
|
static bool set_context_cas (oop site, oop context, oop expected);
|
||||||
|
|
||||||
|
static oop default_context();
|
||||||
|
|
||||||
// Testers
|
// Testers
|
||||||
static bool is_subclass(Klass* klass) {
|
static bool is_subclass(Klass* klass) {
|
||||||
|
@ -1194,7 +1202,6 @@ public:
|
||||||
static int target_offset_in_bytes() { return _target_offset; }
|
static int target_offset_in_bytes() { return _target_offset; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Interface to java.security.AccessControlContext objects
|
// Interface to java.security.AccessControlContext objects
|
||||||
|
|
||||||
class java_security_AccessControlContext: AllStatic {
|
class java_security_AccessControlContext: AllStatic {
|
||||||
|
|
|
@ -292,6 +292,7 @@
|
||||||
template(setTargetNormal_name, "setTargetNormal") \
|
template(setTargetNormal_name, "setTargetNormal") \
|
||||||
template(setTargetVolatile_name, "setTargetVolatile") \
|
template(setTargetVolatile_name, "setTargetVolatile") \
|
||||||
template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \
|
template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \
|
||||||
|
template(DEFAULT_CONTEXT_name, "DEFAULT_CONTEXT") \
|
||||||
NOT_LP64( do_alias(intptr_signature, int_signature) ) \
|
NOT_LP64( do_alias(intptr_signature, int_signature) ) \
|
||||||
LP64_ONLY( do_alias(intptr_signature, long_signature) ) \
|
LP64_ONLY( do_alias(intptr_signature, long_signature) ) \
|
||||||
\
|
\
|
||||||
|
@ -501,6 +502,7 @@
|
||||||
template(class_signature, "Ljava/lang/Class;") \
|
template(class_signature, "Ljava/lang/Class;") \
|
||||||
template(string_signature, "Ljava/lang/String;") \
|
template(string_signature, "Ljava/lang/String;") \
|
||||||
template(reference_signature, "Ljava/lang/ref/Reference;") \
|
template(reference_signature, "Ljava/lang/ref/Reference;") \
|
||||||
|
template(sun_misc_Cleaner_signature, "Lsun/misc/Cleaner;") \
|
||||||
template(executable_signature, "Ljava/lang/reflect/Executable;") \
|
template(executable_signature, "Ljava/lang/reflect/Executable;") \
|
||||||
template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \
|
template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \
|
||||||
template(String_StringBuilder_signature, "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \
|
template(String_StringBuilder_signature, "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \
|
||||||
|
@ -554,7 +556,7 @@
|
||||||
template(createGarbageCollectorMBean_signature, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMBean;") \
|
template(createGarbageCollectorMBean_signature, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMBean;") \
|
||||||
template(trigger_name, "trigger") \
|
template(trigger_name, "trigger") \
|
||||||
template(clear_name, "clear") \
|
template(clear_name, "clear") \
|
||||||
template(trigger_method_signature, "(ILjava/lang/management/MemoryUsage;)V") \
|
template(trigger_method_signature, "(ILjava/lang/management/MemoryUsage;)V") \
|
||||||
template(startAgent_name, "startAgent") \
|
template(startAgent_name, "startAgent") \
|
||||||
template(startRemoteAgent_name, "startRemoteManagementAgent") \
|
template(startRemoteAgent_name, "startRemoteManagementAgent") \
|
||||||
template(startLocalAgent_name, "startLocalManagementAgent") \
|
template(startLocalAgent_name, "startLocalManagementAgent") \
|
||||||
|
|
|
@ -1067,8 +1067,11 @@ void CodeCache::flush_dependents_on(Handle call_site, Handle method_handle) {
|
||||||
int marked = 0;
|
int marked = 0;
|
||||||
{
|
{
|
||||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||||
InstanceKlass* call_site_klass = InstanceKlass::cast(call_site->klass());
|
InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site());
|
||||||
marked = call_site_klass->mark_dependent_nmethods(changes);
|
if (ctxk == NULL) {
|
||||||
|
return; // No dependencies to invalidate yet.
|
||||||
|
}
|
||||||
|
marked = ctxk->mark_dependent_nmethods(changes);
|
||||||
}
|
}
|
||||||
if (marked > 0) {
|
if (marked > 0) {
|
||||||
// At least one nmethod has been marked for deoptimization
|
// At least one nmethod has been marked for deoptimization
|
||||||
|
|
|
@ -117,8 +117,9 @@ void Dependencies::assert_has_no_finalizable_subclasses(ciKlass* ctxk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dependencies::assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle) {
|
void Dependencies::assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle) {
|
||||||
check_ctxk(call_site->klass());
|
ciKlass* ctxk = call_site->get_context();
|
||||||
assert_common_2(call_site_target_value, call_site, method_handle);
|
check_ctxk(ctxk);
|
||||||
|
assert_common_3(call_site_target_value, ctxk, call_site, method_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function. If we are adding a new dep. under ctxk2,
|
// Helper function. If we are adding a new dep. under ctxk2,
|
||||||
|
@ -388,7 +389,7 @@ int Dependencies::_dep_args[TYPE_LIMIT] = {
|
||||||
3, // unique_concrete_subtypes_2 ctxk, k1, k2
|
3, // unique_concrete_subtypes_2 ctxk, k1, k2
|
||||||
3, // unique_concrete_methods_2 ctxk, m1, m2
|
3, // unique_concrete_methods_2 ctxk, m1, m2
|
||||||
1, // no_finalizable_subclasses ctxk
|
1, // no_finalizable_subclasses ctxk
|
||||||
2 // call_site_target_value call_site, method_handle
|
3 // call_site_target_value ctxk, call_site, method_handle
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* Dependencies::dep_name(Dependencies::DepType dept) {
|
const char* Dependencies::dep_name(Dependencies::DepType dept) {
|
||||||
|
@ -594,7 +595,7 @@ void Dependencies::DepStream::log_dependency(Klass* witness) {
|
||||||
const int nargs = argument_count();
|
const int nargs = argument_count();
|
||||||
GrowableArray<DepArgument>* args = new GrowableArray<DepArgument>(nargs);
|
GrowableArray<DepArgument>* args = new GrowableArray<DepArgument>(nargs);
|
||||||
for (int j = 0; j < nargs; j++) {
|
for (int j = 0; j < nargs; j++) {
|
||||||
if (type() == call_site_target_value) {
|
if (is_oop_argument(j)) {
|
||||||
args->push(argument_oop(j));
|
args->push(argument_oop(j));
|
||||||
} else {
|
} else {
|
||||||
args->push(argument(j));
|
args->push(argument(j));
|
||||||
|
@ -614,7 +615,7 @@ void Dependencies::DepStream::print_dependency(Klass* witness, bool verbose) {
|
||||||
int nargs = argument_count();
|
int nargs = argument_count();
|
||||||
GrowableArray<DepArgument>* args = new GrowableArray<DepArgument>(nargs);
|
GrowableArray<DepArgument>* args = new GrowableArray<DepArgument>(nargs);
|
||||||
for (int j = 0; j < nargs; j++) {
|
for (int j = 0; j < nargs; j++) {
|
||||||
if (type() == call_site_target_value) {
|
if (is_oop_argument(j)) {
|
||||||
args->push(argument_oop(j));
|
args->push(argument_oop(j));
|
||||||
} else {
|
} else {
|
||||||
args->push(argument(j));
|
args->push(argument(j));
|
||||||
|
@ -710,7 +711,7 @@ Metadata* Dependencies::DepStream::argument(int i) {
|
||||||
* Returns a unique identifier for each dependency argument.
|
* Returns a unique identifier for each dependency argument.
|
||||||
*/
|
*/
|
||||||
uintptr_t Dependencies::DepStream::get_identifier(int i) {
|
uintptr_t Dependencies::DepStream::get_identifier(int i) {
|
||||||
if (has_oop_argument()) {
|
if (is_oop_argument(i)) {
|
||||||
return (uintptr_t)(oopDesc*)argument_oop(i);
|
return (uintptr_t)(oopDesc*)argument_oop(i);
|
||||||
} else {
|
} else {
|
||||||
return (uintptr_t)argument(i);
|
return (uintptr_t)argument(i);
|
||||||
|
@ -737,7 +738,7 @@ Klass* Dependencies::DepStream::context_type() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some dependencies are using the klass of the first object
|
// Some dependencies are using the klass of the first object
|
||||||
// argument as implicit context type (e.g. call_site_target_value).
|
// argument as implicit context type.
|
||||||
{
|
{
|
||||||
int ctxkj = dep_implicit_context_arg(type());
|
int ctxkj = dep_implicit_context_arg(type());
|
||||||
if (ctxkj >= 0) {
|
if (ctxkj >= 0) {
|
||||||
|
@ -1514,9 +1515,16 @@ Klass* Dependencies::check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepCh
|
||||||
return find_finalizable_subclass(search_at);
|
return find_finalizable_subclass(search_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes) {
|
Klass* Dependencies::check_call_site_target_value(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes) {
|
||||||
assert(call_site ->is_a(SystemDictionary::CallSite_klass()), "sanity");
|
assert(call_site->is_a(SystemDictionary::CallSite_klass()), "sanity");
|
||||||
assert(method_handle->is_a(SystemDictionary::MethodHandle_klass()), "sanity");
|
assert(!oopDesc::is_null(method_handle), "sanity");
|
||||||
|
|
||||||
|
Klass* call_site_ctxk = MethodHandles::get_call_site_context(call_site);
|
||||||
|
assert(!Klass::is_null(call_site_ctxk), "call site context should be initialized already");
|
||||||
|
if (recorded_ctxk != call_site_ctxk) {
|
||||||
|
// Stale context
|
||||||
|
return recorded_ctxk;
|
||||||
|
}
|
||||||
if (changes == NULL) {
|
if (changes == NULL) {
|
||||||
// Validate all CallSites
|
// Validate all CallSites
|
||||||
if (java_lang_invoke_CallSite::target(call_site) != method_handle)
|
if (java_lang_invoke_CallSite::target(call_site) != method_handle)
|
||||||
|
@ -1531,7 +1539,6 @@ Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_hand
|
||||||
return NULL; // assertion still valid
|
return NULL; // assertion still valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Dependencies::DepStream::trace_and_log_witness(Klass* witness) {
|
void Dependencies::DepStream::trace_and_log_witness(Klass* witness) {
|
||||||
if (witness != NULL) {
|
if (witness != NULL) {
|
||||||
if (TraceDependencies) {
|
if (TraceDependencies) {
|
||||||
|
@ -1592,7 +1599,7 @@ Klass* Dependencies::DepStream::check_call_site_dependency(CallSiteDepChange* ch
|
||||||
Klass* witness = NULL;
|
Klass* witness = NULL;
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
case call_site_target_value:
|
case call_site_target_value:
|
||||||
witness = check_call_site_target_value(argument_oop(0), argument_oop(1), changes);
|
witness = check_call_site_target_value(context_type(), argument_oop(1), argument_oop(2), changes);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
witness = NULL;
|
witness = NULL;
|
||||||
|
|
|
@ -174,7 +174,7 @@ class Dependencies: public ResourceObj {
|
||||||
klass_types = all_types & ~non_klass_types,
|
klass_types = all_types & ~non_klass_types,
|
||||||
|
|
||||||
non_ctxk_types = (1 << evol_method),
|
non_ctxk_types = (1 << evol_method),
|
||||||
implicit_ctxk_types = (1 << call_site_target_value),
|
implicit_ctxk_types = 0,
|
||||||
explicit_ctxk_types = all_types & ~(non_ctxk_types | implicit_ctxk_types),
|
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 = 3, // current maximum number of arguments (incl. ctxk)
|
||||||
|
@ -330,7 +330,7 @@ class Dependencies: public ResourceObj {
|
||||||
static Klass* check_exclusive_concrete_methods(Klass* ctxk, Method* m1, Method* m2,
|
static Klass* check_exclusive_concrete_methods(Klass* ctxk, Method* m1, Method* m2,
|
||||||
KlassDepChange* changes = NULL);
|
KlassDepChange* changes = NULL);
|
||||||
static Klass* check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepChange* changes = NULL);
|
static Klass* check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepChange* changes = NULL);
|
||||||
static Klass* check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
|
static Klass* check_call_site_target_value(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
|
||||||
// A returned Klass* is NULL if the dependency assertion is still
|
// A returned Klass* is NULL if the dependency assertion is still
|
||||||
// valid. A non-NULL Klass* is a 'witness' to the assertion
|
// valid. A non-NULL Klass* is a 'witness' to the assertion
|
||||||
// failure, a point in the class hierarchy where the assertion has
|
// failure, a point in the class hierarchy where the assertion has
|
||||||
|
@ -496,7 +496,7 @@ class Dependencies: public ResourceObj {
|
||||||
bool next();
|
bool next();
|
||||||
|
|
||||||
DepType type() { return _type; }
|
DepType type() { return _type; }
|
||||||
bool has_oop_argument() { return type() == call_site_target_value; }
|
bool is_oop_argument(int i) { return type() == call_site_target_value && i > 0; }
|
||||||
uintptr_t get_identifier(int i);
|
uintptr_t get_identifier(int i);
|
||||||
|
|
||||||
int argument_count() { return dep_args(type()); }
|
int argument_count() { return dep_args(type()); }
|
||||||
|
@ -682,7 +682,7 @@ class CallSiteDepChange : public DepChange {
|
||||||
_method_handle(method_handle)
|
_method_handle(method_handle)
|
||||||
{
|
{
|
||||||
assert(_call_site() ->is_a(SystemDictionary::CallSite_klass()), "must be");
|
assert(_call_site() ->is_a(SystemDictionary::CallSite_klass()), "must be");
|
||||||
assert(_method_handle()->is_a(SystemDictionary::MethodHandle_klass()), "must be");
|
assert(_method_handle.is_null() || _method_handle()->is_a(SystemDictionary::MethodHandle_klass()), "must be");
|
||||||
}
|
}
|
||||||
|
|
||||||
// What kind of DepChange is this?
|
// What kind of DepChange is this?
|
||||||
|
|
|
@ -2325,6 +2325,7 @@ void nmethod::check_all_dependencies(DepChange& changes) {
|
||||||
// Dependency checking failed. Print out information about the failed
|
// Dependency checking failed. Print out information about the failed
|
||||||
// dependency and finally fail with an assert. We can fail here, since
|
// dependency and finally fail with an assert. We can fail here, since
|
||||||
// dependency checking is never done in a product build.
|
// dependency checking is never done in a product build.
|
||||||
|
tty->print_cr("Failed dependency:");
|
||||||
changes.print();
|
changes.print();
|
||||||
nm->print();
|
nm->print();
|
||||||
nm->print_dependencies();
|
nm->print_dependencies();
|
||||||
|
|
|
@ -939,6 +939,24 @@ int MethodHandles::find_MemberNames(KlassHandle k,
|
||||||
return rfill + overflow;
|
return rfill + overflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get context class for a CallSite instance: either extract existing context or use default one.
|
||||||
|
InstanceKlass* MethodHandles::get_call_site_context(oop call_site) {
|
||||||
|
// In order to extract a context the following traversal is performed:
|
||||||
|
// CallSite.context => Cleaner.referent => Class._klass => Klass
|
||||||
|
assert(java_lang_invoke_CallSite::is_instance(call_site), "");
|
||||||
|
oop context_oop = java_lang_invoke_CallSite::context_volatile(call_site);
|
||||||
|
if (oopDesc::is_null(context_oop)) {
|
||||||
|
return NULL; // The context hasn't been initialized yet.
|
||||||
|
}
|
||||||
|
oop context_class_oop = java_lang_ref_Reference::referent(context_oop);
|
||||||
|
if (oopDesc::is_null(context_class_oop)) {
|
||||||
|
// The context reference was cleared by GC, so current dependency context
|
||||||
|
// isn't usable anymore. Context should be fetched from CallSite again.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return InstanceKlass::cast(java_lang_Class::as_Klass(context_class_oop));
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// MemberNameTable
|
// MemberNameTable
|
||||||
//
|
//
|
||||||
|
@ -1231,7 +1249,7 @@ JVM_END
|
||||||
|
|
||||||
JVM_ENTRY(void, MHN_setCallSiteTargetNormal(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
|
JVM_ENTRY(void, MHN_setCallSiteTargetNormal(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
|
||||||
Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
|
Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
|
||||||
Handle target (THREAD, JNIHandles::resolve(target_jh));
|
Handle target (THREAD, JNIHandles::resolve_non_null(target_jh));
|
||||||
{
|
{
|
||||||
// Walk all nmethods depending on this call site.
|
// Walk all nmethods depending on this call site.
|
||||||
MutexLocker mu(Compile_lock, thread);
|
MutexLocker mu(Compile_lock, thread);
|
||||||
|
@ -1243,7 +1261,7 @@ JVM_END
|
||||||
|
|
||||||
JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
|
JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
|
||||||
Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
|
Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
|
||||||
Handle target (THREAD, JNIHandles::resolve(target_jh));
|
Handle target (THREAD, JNIHandles::resolve_non_null(target_jh));
|
||||||
{
|
{
|
||||||
// Walk all nmethods depending on this call site.
|
// Walk all nmethods depending on this call site.
|
||||||
MutexLocker mu(Compile_lock, thread);
|
MutexLocker mu(Compile_lock, thread);
|
||||||
|
@ -1253,6 +1271,33 @@ JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobjec
|
||||||
}
|
}
|
||||||
JVM_END
|
JVM_END
|
||||||
|
|
||||||
|
JVM_ENTRY(void, MHN_invalidateDependentNMethods(JNIEnv* env, jobject igcls, jobject call_site_jh)) {
|
||||||
|
Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
|
||||||
|
{
|
||||||
|
// Walk all nmethods depending on this call site.
|
||||||
|
MutexLocker mu1(Compile_lock, thread);
|
||||||
|
|
||||||
|
CallSiteDepChange changes(call_site(), Handle());
|
||||||
|
|
||||||
|
InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site());
|
||||||
|
if (ctxk == NULL) {
|
||||||
|
return; // No dependencies to invalidate yet.
|
||||||
|
}
|
||||||
|
int marked = 0;
|
||||||
|
{
|
||||||
|
MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
marked = ctxk->mark_dependent_nmethods(changes);
|
||||||
|
}
|
||||||
|
java_lang_invoke_CallSite::set_context_volatile(call_site(), NULL); // Reset call site to initial state
|
||||||
|
if (marked > 0) {
|
||||||
|
// At least one nmethod has been marked for deoptimization
|
||||||
|
VM_Deoptimize op;
|
||||||
|
VMThread::execute(&op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JVM_END
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws a java/lang/UnsupportedOperationException unconditionally.
|
* Throws a java/lang/UnsupportedOperationException unconditionally.
|
||||||
* This is required by the specification of MethodHandle.invoke if
|
* This is required by the specification of MethodHandle.invoke if
|
||||||
|
@ -1306,6 +1351,7 @@ static JNINativeMethod MHN_methods[] = {
|
||||||
{CC"objectFieldOffset", CC"("MEM")J", FN_PTR(MHN_objectFieldOffset)},
|
{CC"objectFieldOffset", CC"("MEM")J", FN_PTR(MHN_objectFieldOffset)},
|
||||||
{CC"setCallSiteTargetNormal", CC"("CS""MH")V", FN_PTR(MHN_setCallSiteTargetNormal)},
|
{CC"setCallSiteTargetNormal", CC"("CS""MH")V", FN_PTR(MHN_setCallSiteTargetNormal)},
|
||||||
{CC"setCallSiteTargetVolatile", CC"("CS""MH")V", FN_PTR(MHN_setCallSiteTargetVolatile)},
|
{CC"setCallSiteTargetVolatile", CC"("CS""MH")V", FN_PTR(MHN_setCallSiteTargetVolatile)},
|
||||||
|
{CC"invalidateDependentNMethods", CC"("CS")V", FN_PTR(MHN_invalidateDependentNMethods)},
|
||||||
{CC"staticFieldOffset", CC"("MEM")J", FN_PTR(MHN_staticFieldOffset)},
|
{CC"staticFieldOffset", CC"("MEM")J", FN_PTR(MHN_staticFieldOffset)},
|
||||||
{CC"staticFieldBase", CC"("MEM")"OBJ, FN_PTR(MHN_staticFieldBase)},
|
{CC"staticFieldBase", CC"("MEM")"OBJ, FN_PTR(MHN_staticFieldBase)},
|
||||||
{CC"getMemberVMInfo", CC"("MEM")"OBJ, FN_PTR(MHN_getMemberVMInfo)}
|
{CC"getMemberVMInfo", CC"("MEM")"OBJ, FN_PTR(MHN_getMemberVMInfo)}
|
||||||
|
|
|
@ -68,6 +68,9 @@ class MethodHandles: AllStatic {
|
||||||
// bit values for suppress argument to expand_MemberName:
|
// bit values for suppress argument to expand_MemberName:
|
||||||
enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 };
|
enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 };
|
||||||
|
|
||||||
|
// CallSite support
|
||||||
|
static InstanceKlass* get_call_site_context(oop call_site);
|
||||||
|
|
||||||
// Generate MethodHandles adapters.
|
// Generate MethodHandles adapters.
|
||||||
static bool generate_adapters();
|
static bool generate_adapters();
|
||||||
|
|
||||||
|
|
179
hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
Normal file
179
hotspot/test/compiler/jsr292/CallSiteDepContextTest.java
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Oracle and/or its affiliates. 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 8057967
|
||||||
|
* @run main/bootclasspath -Xbatch java.lang.invoke.CallSiteDepContextTest
|
||||||
|
*/
|
||||||
|
package java.lang.invoke;
|
||||||
|
|
||||||
|
import java.lang.ref.*;
|
||||||
|
import jdk.internal.org.objectweb.asm.*;
|
||||||
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
public class CallSiteDepContextTest {
|
||||||
|
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||||
|
static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP;
|
||||||
|
static final String CLASS_NAME = "java/lang/invoke/Test";
|
||||||
|
static final String METHOD_NAME = "m";
|
||||||
|
static final MethodType TYPE = MethodType.methodType(int.class);
|
||||||
|
|
||||||
|
static MutableCallSite mcs;
|
||||||
|
static MethodHandle bsmMH;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
bsmMH = LOOKUP.findStatic(
|
||||||
|
CallSiteDepContextTest.class, "bootstrap",
|
||||||
|
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class));
|
||||||
|
} catch(Throwable e) {
|
||||||
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CallSite bootstrap(MethodHandles.Lookup caller,
|
||||||
|
String invokedName,
|
||||||
|
MethodType invokedType) {
|
||||||
|
return mcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class T {
|
||||||
|
static int f1() { return 1; }
|
||||||
|
static int f2() { return 2; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] getClassFile(String suffix) {
|
||||||
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
MethodVisitor mv;
|
||||||
|
cw.visit(52, ACC_PUBLIC | ACC_SUPER, CLASS_NAME + suffix, null, "java/lang/Object", null);
|
||||||
|
{
|
||||||
|
mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, METHOD_NAME, TYPE.toMethodDescriptorString(), null, null);
|
||||||
|
mv.visitCode();
|
||||||
|
Handle bsm = new Handle(H_INVOKESTATIC,
|
||||||
|
"java/lang/invoke/CallSiteDepContextTest", "bootstrap",
|
||||||
|
bsmMH.type().toMethodDescriptorString());
|
||||||
|
mv.visitInvokeDynamicInsn("methodName", TYPE.toMethodDescriptorString(), bsm);
|
||||||
|
mv.visitInsn(IRETURN);
|
||||||
|
mv.visitMaxs(0, 0);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
cw.visitEnd();
|
||||||
|
return cw.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void execute(int expected, MethodHandle... mhs) throws Throwable {
|
||||||
|
for (int i = 0; i < 20_000; i++) {
|
||||||
|
for (MethodHandle mh : mhs) {
|
||||||
|
int r = (int) mh.invokeExact();
|
||||||
|
if (r != expected) {
|
||||||
|
throw new Error(r + " != " + expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testSharedCallSite() throws Throwable {
|
||||||
|
Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null);
|
||||||
|
Class<?> cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null);
|
||||||
|
|
||||||
|
MethodHandle[] mhs = new MethodHandle[] {
|
||||||
|
LOOKUP.findStatic(cls1, METHOD_NAME, TYPE),
|
||||||
|
LOOKUP.findStatic(cls2, METHOD_NAME, TYPE)
|
||||||
|
};
|
||||||
|
|
||||||
|
mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
|
||||||
|
execute(1, mhs);
|
||||||
|
mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
|
||||||
|
execute(2, mhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testNonBoundCallSite() throws Throwable {
|
||||||
|
mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
|
||||||
|
|
||||||
|
// mcs.context == null
|
||||||
|
MethodHandle mh = mcs.dynamicInvoker();
|
||||||
|
execute(1, mh);
|
||||||
|
|
||||||
|
// mcs.context == cls1
|
||||||
|
Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("NonBound_1"), null);
|
||||||
|
MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE);
|
||||||
|
|
||||||
|
execute(1, mh1);
|
||||||
|
|
||||||
|
mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
|
||||||
|
|
||||||
|
execute(2, mh, mh1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ReferenceQueue rq = new ReferenceQueue();
|
||||||
|
static PhantomReference ref;
|
||||||
|
|
||||||
|
public static void testGC() throws Throwable {
|
||||||
|
mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
|
||||||
|
|
||||||
|
Class<?>[] cls = new Class[] {
|
||||||
|
UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1"), null),
|
||||||
|
UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2"), null),
|
||||||
|
};
|
||||||
|
|
||||||
|
MethodHandle[] mhs = new MethodHandle[] {
|
||||||
|
LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE),
|
||||||
|
LOOKUP.findStatic(cls[1], METHOD_NAME, TYPE),
|
||||||
|
};
|
||||||
|
|
||||||
|
// mcs.context == cls[0]
|
||||||
|
int r = (int) mhs[0].invokeExact();
|
||||||
|
|
||||||
|
execute(1, mhs);
|
||||||
|
|
||||||
|
ref = new PhantomReference<>(cls[0], rq);
|
||||||
|
cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3"), null);
|
||||||
|
mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE);
|
||||||
|
|
||||||
|
do {
|
||||||
|
System.gc();
|
||||||
|
try {
|
||||||
|
Reference ref1 = rq.remove(1000);
|
||||||
|
if (ref1 == ref) {
|
||||||
|
ref1.clear();
|
||||||
|
System.gc(); // Ensure that the stale context is cleared
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch(InterruptedException e) { /* ignore */ }
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
execute(1, mhs);
|
||||||
|
mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
|
||||||
|
execute(2, mhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
testSharedCallSite();
|
||||||
|
testNonBoundCallSite();
|
||||||
|
testGC();
|
||||||
|
System.out.println("TEST PASSED");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue