# HG changeset patch # Parent a1750ee2758e9c3b1eabcccd555fab12282bdda8 diff --git a/src/share/vm/ci/ciCallSite.cpp b/src/share/vm/ci/ciCallSite.cpp --- a/src/share/vm/ci/ciCallSite.cpp +++ b/src/share/vm/ci/ciCallSite.cpp @@ -49,6 +49,29 @@ } // ------------------------------------------------------------------ +// ciCallSite::get_context +// +// Return the target MethodHandle of this CallSite. +ciKlass* ciCallSite::get_context() /*const*/ { + VM_ENTRY_MARK; + oop context_oop; + if (is_mutable_call_site()) { + context_oop = java_lang_invoke_MutableCallSite::context(get_oop()); + } else if (is_volatile_call_site()) { + // TODO + } else { + fatal("ConstantCallSite doesn't need a context"); + } + Klass* ctxk; + if (context_oop != NULL) { + ctxk = java_lang_Class::as_Klass(context_oop); + } else { + ctxk = get_oop()->klass(); + } + return (CURRENT_ENV->get_metadata(ctxk))->as_klass(); +} + +// ------------------------------------------------------------------ // ciCallSite::print // // Print debugging information about the CallSite. diff --git a/src/share/vm/ci/ciCallSite.hpp b/src/share/vm/ci/ciCallSite.hpp --- a/src/share/vm/ci/ciCallSite.hpp +++ b/src/share/vm/ci/ciCallSite.hpp @@ -43,6 +43,7 @@ // Return the target MethodHandle of this CallSite. ciMethodHandle* get_target() const; + ciKlass* get_context() /*const*/; void print(); }; diff --git a/src/share/vm/classfile/javaClasses.cpp b/src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp +++ b/src/share/vm/classfile/javaClasses.cpp @@ -2948,6 +2948,16 @@ } } +// Support for java_lang_invoke_MutableCallSite + +int java_lang_invoke_MutableCallSite::_context_offset; + +void java_lang_invoke_MutableCallSite::compute_offsets() { + Klass* k = SystemDictionary::MutableCallSite_klass(); + if (k != NULL) { + compute_offset(_context_offset, k, vmSymbols::context_name(), vmSymbols::class_signature()); + } +} // Support for java_security_AccessControlContext @@ -3338,6 +3348,7 @@ java_lang_invoke_LambdaForm::compute_offsets(); java_lang_invoke_MethodType::compute_offsets(); java_lang_invoke_CallSite::compute_offsets(); + java_lang_invoke_MutableCallSite::compute_offsets(); java_security_AccessControlContext::compute_offsets(); // Initialize reflection classes. The layouts of these classes // changed with the new reflection implementation in JDK 1.4, and diff --git a/src/share/vm/classfile/javaClasses.hpp b/src/share/vm/classfile/javaClasses.hpp --- a/src/share/vm/classfile/javaClasses.hpp +++ b/src/share/vm/classfile/javaClasses.hpp @@ -1205,6 +1205,31 @@ static int target_offset_in_bytes() { return _target_offset; } }; +class java_lang_invoke_MutableCallSite: AllStatic { + friend class JavaClasses; + +private: + static int _context_offset; + + static void compute_offsets(); + +public: + // Accessors + static oop context(oop site) { return site->obj_field(_context_offset); } + static void set_context(oop site, oop context) { site->obj_field_put(_context_offset, context); } + + // Testers + static bool is_subclass(Klass* klass) { + return klass->is_subclass_of(SystemDictionary::CallSite_klass()); + } + static bool is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); + } + + // Accessors for code generation: + static int context_offset_in_bytes() { return _context_offset; } +}; + // Interface to java.security.AccessControlContext objects diff --git a/src/share/vm/code/codeCache.cpp b/src/share/vm/code/codeCache.cpp --- a/src/share/vm/code/codeCache.cpp +++ b/src/share/vm/code/codeCache.cpp @@ -872,7 +872,8 @@ No_Safepoint_Verifier nsv; for (DepChange::ContextStream str(changes, nsv); str.next(); ) { Klass* d = str.klass(); - number_of_marked_CodeBlobs += InstanceKlass::cast(d)->mark_dependent_nmethods(changes); + int total = 0; + number_of_marked_CodeBlobs += InstanceKlass::cast(d)->mark_dependent_nmethods(changes, total); } #ifndef PRODUCT diff --git a/src/share/vm/code/dependencies.cpp b/src/share/vm/code/dependencies.cpp --- a/src/share/vm/code/dependencies.cpp +++ b/src/share/vm/code/dependencies.cpp @@ -116,8 +116,15 @@ } void Dependencies::assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle) { - check_ctxk(call_site->klass()); - assert_common_2(call_site_target_value, call_site, method_handle); + ciKlass* ctxk = NULL; + switch (CallSiteDependencyType) { + case 0: ctxk = call_site->klass(); break; + case 1: ctxk = method_handle->get_vmtarget()->holder(); break; + case 2: ctxk = call_site->get_context(); break; + default: fatal(""); + } + 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, @@ -387,7 +394,7 @@ 3, // unique_concrete_subtypes_2 ctxk, k1, k2 3, // unique_concrete_methods_2 ctxk, m1, m2 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) { @@ -593,7 +600,7 @@ const int nargs = argument_count(); GrowableArray* args = new GrowableArray(nargs); for (int j = 0; j < nargs; j++) { - if (type() == call_site_target_value) { + if (is_oop_argument(j)) { args->push(argument_oop(j)); } else { args->push(argument(j)); @@ -613,7 +620,7 @@ int nargs = argument_count(); GrowableArray* args = new GrowableArray(nargs); for (int j = 0; j < nargs; j++) { - if (type() == call_site_target_value) { + if (is_oop_argument(j)) { args->push(argument_oop(j)); } else { args->push(argument(j)); @@ -709,7 +716,7 @@ * Returns a unique identifier for each dependency argument. */ uintptr_t Dependencies::DepStream::get_identifier(int i) { - if (has_oop_argument()) { + if (is_oop_argument(i)) { return (uintptr_t)(oopDesc*)argument_oop(i); } else { return (uintptr_t)argument(i); @@ -736,7 +743,7 @@ } // 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()); if (ctxkj >= 0) { @@ -1636,7 +1643,7 @@ Klass* witness = NULL; switch (type()) { case call_site_target_value: - witness = check_call_site_target_value(argument_oop(0), argument_oop(1), changes); + witness = check_call_site_target_value(argument_oop(1), argument_oop(2), changes); break; default: witness = NULL; diff --git a/src/share/vm/code/dependencies.hpp b/src/share/vm/code/dependencies.hpp --- a/src/share/vm/code/dependencies.hpp +++ b/src/share/vm/code/dependencies.hpp @@ -173,7 +173,7 @@ klass_types = all_types & ~non_klass_types, 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), max_arg_count = 3, // current maximum number of arguments (incl. ctxk) @@ -497,7 +497,7 @@ bool next(); 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); int argument_count() { return dep_args(type()); } diff --git a/src/share/vm/code/nmethod.cpp b/src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp +++ b/src/share/vm/code/nmethod.cpp @@ -2513,6 +2513,7 @@ // Dependency checking failed. Print out information about the failed // dependency and finally fail with an assert. We can fail here, since // dependency checking is never done in a product build. + tty->print_cr("Failed dependency:"); changes.print(); nm->print(); nm->print_dependencies(); diff --git a/src/share/vm/memory/universe.cpp b/src/share/vm/memory/universe.cpp --- a/src/share/vm/memory/universe.cpp +++ b/src/share/vm/memory/universe.cpp @@ -1217,16 +1217,72 @@ CallSiteDepChange changes(call_site(), method_handle()); + /* + if (TraceDependencies) { + tty->print_cr("Call site change: "); + tty->print(" call site = "); + call_site()->print_value_on(tty); + tty->print(" target = "); + method_handle()->print_value_on(tty); + } + */ // Compute the dependent nmethods that have a reference to a // CallSite object. We use InstanceKlass::mark_dependent_nmethod // directly instead of CodeCache::mark_for_deoptimization because we // want dependents on the call site class only not all classes in // the ContextStream. int marked = 0; + int total = 0; + InstanceKlass* method_klass = NULL; + Metadata* vmtarget = NULL; { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - InstanceKlass* call_site_klass = InstanceKlass::cast(call_site->klass()); - marked = call_site_klass->mark_dependent_nmethods(changes); + + switch (CallSiteDependencyType) { + case 0: { // CallSite klass + method_klass = InstanceKlass::cast(call_site->klass()); + break; + } + case 1: { // Inlinee klass + oop form_oop = java_lang_invoke_MethodHandle::form(method_handle()); + oop vmentry_oop = java_lang_invoke_LambdaForm::vmentry(form_oop); + vmtarget = java_lang_invoke_MemberName::vmtarget(vmentry_oop); + if (vmtarget->is_method()) { + method_klass = ((Method*)vmtarget)->method_holder(); + } else { + // FIXME: What if the vmtarget is a Klass? + ShouldNotReachHere(); + } + break; + } + case 2: { // Dedicated klass + oop context_oop = NULL; + java_lang_invoke_MutableCallSite::context(call_site()); + if (context_oop != NULL) { + method_klass = InstanceKlass::cast(java_lang_Class::as_Klass(context_oop)); + } else { + method_klass = InstanceKlass::cast(call_site->klass()); + } + break; + } + default: + fatal(""); + } + marked = method_klass->mark_dependent_nmethods(changes, total); + } + static int marked_all = 0; + static int total_all = 0; + static int no_of_checks = 0; + if (TraceCallSiteChanges) { + marked_all += marked; + total_all += total; + no_of_checks++; + tty->print_cr("CallSiteDepChange %d cs{"INTPTR_FORMAT"} %s::%s ("INTPTR_FORMAT") %d / %d (%d / %d)", + no_of_checks, + call_site(), method_klass->name()->as_C_string(), + vmtarget != NULL ? ((Method*)vmtarget)->name()->as_C_string() : "NULL", + method_klass, + marked, total, marked_all, total_all); } if (marked > 0) { // At least one nmethod has been marked for deoptimization diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp --- a/src/share/vm/oops/instanceKlass.cpp +++ b/src/share/vm/oops/instanceKlass.cpp @@ -1813,11 +1813,13 @@ // are dependent on the changes that were passed in and mark them for // deoptimization. Returns the number of nmethods found. // -int InstanceKlass::mark_dependent_nmethods(DepChange& changes) { +int InstanceKlass::mark_dependent_nmethods(DepChange& changes, int& total) { assert_locked_or_safepoint(CodeCache_lock); int found = 0; + int cnt = 0; nmethodBucket* b = _dependencies; while (b != NULL) { + cnt++; nmethod* nm = b->get_nmethod(); // since dependencies aren't removed until an nmethod becomes a zombie, // the dependency list may contain nmethods which aren't alive. @@ -1835,6 +1837,7 @@ } b = b->next(); } + total = cnt; return found; } diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp --- a/src/share/vm/oops/instanceKlass.hpp +++ b/src/share/vm/oops/instanceKlass.hpp @@ -733,7 +733,7 @@ JNIid* jni_id_for(int offset); // maintenance of deoptimization dependencies - int mark_dependent_nmethods(DepChange& changes); + int mark_dependent_nmethods(DepChange& changes, int& total); void add_dependent_nmethod(nmethod* nm); void remove_dependent_nmethod(nmethod* nm); diff --git a/src/share/vm/prims/methodHandles.cpp b/src/share/vm/prims/methodHandles.cpp --- a/src/share/vm/prims/methodHandles.cpp +++ b/src/share/vm/prims/methodHandles.cpp @@ -1270,7 +1270,7 @@ 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 target (THREAD, JNIHandles::resolve(target_jh)); + Handle target (THREAD, JNIHandles::resolve_non_null(target_jh)); { // Walk all nmethods depending on this call site. MutexLocker mu(Compile_lock, thread); @@ -1282,7 +1282,7 @@ 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 target (THREAD, JNIHandles::resolve(target_jh)); + Handle target (THREAD, JNIHandles::resolve_non_null(target_jh)); { // Walk all nmethods depending on this call site. MutexLocker mu(Compile_lock, thread); diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -3862,6 +3862,12 @@ develop(bool, TraceInvokeDynamic, false, \ "trace internal invoke dynamic operations") \ \ + product(intx, CallSiteDependencyType, 0, \ + "0 - call site class; 1 - target method holder; 2 - dedicated class") \ + \ + product(bool, TraceCallSiteChanges, false, \ + "Trace mutable call site changes") \ + \ diagnostic(bool, PauseAtStartup, false, \ "Causes the VM to pause at startup time and wait for the pause " \ "file to be removed (default: ./vm.paused.)") \