diff --git a/Revert-Backport-8143408-8139595.patch b/Revert-Backport-8143408-8139595.patch new file mode 100644 index 0000000000000000000000000000000000000000..5afea7669b5202a24e8a7a3c84d1c4c8e37504a8 --- /dev/null +++ b/Revert-Backport-8143408-8139595.patch @@ -0,0 +1,1212 @@ +diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp +index 3cd48e30d..423337271 100644 +--- a/hotspot/src/share/vm/classfile/javaClasses.cpp ++++ b/hotspot/src/share/vm/classfile/javaClasses.cpp +@@ -28,7 +28,6 @@ + #include "classfile/symbolTable.hpp" + #include "classfile/vmSymbols.hpp" + #include "code/debugInfo.hpp" +-#include "code/dependencyContext.hpp" + #include "code/pcDesc.hpp" + #include "compiler/compilerOracle.hpp" + #include "interpreter/interpreter.hpp" +@@ -3033,16 +3032,14 @@ void java_lang_invoke_MethodHandleNatives_CallSiteContext::compute_offsets() { + } + } + +-DependencyContext java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(oop call_site) { ++nmethodBucket* java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(oop call_site) { + assert(java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(call_site), ""); +- intptr_t* vmdeps_addr = (intptr_t*)call_site->address_field_addr(_vmdependencies_offset); +-#ifndef ASSERT +- DependencyContext dep_ctx(vmdeps_addr); +-#else +- // Verify that call_site isn't moved during DependencyContext lifetime. +- DependencyContext dep_ctx(vmdeps_addr, Handle(call_site)); +-#endif // ASSERT +- return dep_ctx; ++ return (nmethodBucket*) (address) call_site->long_field(_vmdependencies_offset); ++} ++ ++void java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(oop call_site, nmethodBucket* context) { ++ assert(java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(call_site), ""); ++ call_site->long_field_put(_vmdependencies_offset, (jlong) (address) context); + } + + // Support for java_security_AccessControlContext +diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp +index d8dda1dfc..c41238138 100644 +--- a/hotspot/src/share/vm/classfile/javaClasses.hpp ++++ b/hotspot/src/share/vm/classfile/javaClasses.hpp +@@ -1243,8 +1243,6 @@ public: + #define CALLSITECONTEXT_INJECTED_FIELDS(macro) \ + macro(java_lang_invoke_MethodHandleNatives_CallSiteContext, vmdependencies, intptr_signature, false) + +-class DependencyContext; +- + class java_lang_invoke_MethodHandleNatives_CallSiteContext : AllStatic { + friend class JavaClasses; + +@@ -1255,7 +1253,8 @@ private: + + public: + // Accessors +- static DependencyContext vmdependencies(oop context); ++ static nmethodBucket* vmdependencies(oop context); ++ static void set_vmdependencies(oop context, nmethodBucket* bucket); + + // Testers + static bool is_subclass(Klass* klass) { +diff --git a/hotspot/src/share/vm/code/dependencyContext.cpp b/hotspot/src/share/vm/code/dependencyContext.cpp +deleted file mode 100644 +index 33e629c86..000000000 +--- a/hotspot/src/share/vm/code/dependencyContext.cpp ++++ /dev/null +@@ -1,351 +0,0 @@ +-/* +- * 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. +- * +- */ +- +-#include "precompiled.hpp" +-#include "code/nmethod.hpp" +-#include "code/dependencies.hpp" +-#include "code/dependencyContext.hpp" +-#include "memory/resourceArea.hpp" +-#include "runtime/atomic.hpp" +-#include "runtime/perfData.hpp" +-#include "utilities/exceptions.hpp" +- +-PerfCounter* DependencyContext::_perf_total_buckets_allocated_count = NULL; +-PerfCounter* DependencyContext::_perf_total_buckets_deallocated_count = NULL; +-PerfCounter* DependencyContext::_perf_total_buckets_stale_count = NULL; +-PerfCounter* DependencyContext::_perf_total_buckets_stale_acc_count = NULL; +- +-void dependencyContext_init() { +- DependencyContext::init(); +-} +- +-void DependencyContext::init() { +- if (UsePerfData) { +- EXCEPTION_MARK; +- _perf_total_buckets_allocated_count = +- PerfDataManager::create_counter(SUN_CI, "nmethodBucketsAllocated", PerfData::U_Events, CHECK); +- _perf_total_buckets_deallocated_count = +- PerfDataManager::create_counter(SUN_CI, "nmethodBucketsDeallocated", PerfData::U_Events, CHECK); +- _perf_total_buckets_stale_count = +- PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStale", PerfData::U_Events, CHECK); +- _perf_total_buckets_stale_acc_count = +- PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStaleAccumulated", PerfData::U_Events, CHECK); +- } +-} +- +-// +-// Walk the list of dependent nmethods searching for nmethods which +-// are dependent on the changes that were passed in and mark them for +-// deoptimization. Returns the number of nmethods found. +-// +-int DependencyContext::mark_dependent_nmethods(DepChange& changes) { +- int found = 0; +- for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { +- 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. +- if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) { +- if (TraceDependencies) { +- ResourceMark rm; +- tty->print_cr("Marked for deoptimization"); +- changes.print(); +- nm->print(); +- nm->print_dependencies(); +- } +- nm->mark_for_deoptimization(); +- found++; +- } +- } +- return found; +-} +- +-// +-// Add an nmethod to the dependency context. +-// It's possible that an nmethod has multiple dependencies on a klass +-// so a count is kept for each bucket to guarantee that creation and +-// deletion of dependencies is consistent. +-// +-void DependencyContext::add_dependent_nmethod(nmethod* nm, bool expunge) { +- assert_lock_strong(CodeCache_lock); +- for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { +- if (nm == b->get_nmethod()) { +- b->increment(); +- return; +- } +- } +- set_dependencies(new nmethodBucket(nm, dependencies())); +- if (UsePerfData) { +- _perf_total_buckets_allocated_count->inc(); +- } +- if (expunge) { +- // Remove stale entries from the list. +- expunge_stale_entries(); +- } +-} +- +-// +-// Remove an nmethod dependency from the context. +-// Decrement count of the nmethod in the dependency list and, optionally, remove +-// the bucket completely when the count goes to 0. This method must find +-// a corresponding bucket otherwise there's a bug in the recording of dependencies. +-// Can be called concurrently by parallel GC threads. +-// +-void DependencyContext::remove_dependent_nmethod(nmethod* nm, bool expunge) { +- assert_locked_or_safepoint(CodeCache_lock); +- nmethodBucket* first = dependencies(); +- nmethodBucket* last = NULL; +- for (nmethodBucket* b = first; b != NULL; b = b->next()) { +- if (nm == b->get_nmethod()) { +- int val = b->decrement(); +- guarantee(val >= 0, err_msg("Underflow: %d", val)); +- if (val == 0) { +- if (expunge) { +- if (last == NULL) { +- set_dependencies(b->next()); +- } else { +- last->set_next(b->next()); +- } +- delete b; +- if (UsePerfData) { +- _perf_total_buckets_deallocated_count->inc(); +- } +- } else { +- // Mark the context as having stale entries, since it is not safe to +- // expunge the list right now. +- set_has_stale_entries(true); +- if (UsePerfData) { +- _perf_total_buckets_stale_count->inc(); +- _perf_total_buckets_stale_acc_count->inc(); +- } +- } +- } +- if (expunge) { +- // Remove stale entries from the list. +- expunge_stale_entries(); +- } +- return; +- } +- last = b; +- } +-#ifdef ASSERT +- tty->print_raw_cr("### can't find dependent nmethod"); +- nm->print(); +-#endif // ASSERT +- ShouldNotReachHere(); +-} +- +-// +-// Reclaim all unused buckets. +-// +-void DependencyContext::expunge_stale_entries() { +- assert_locked_or_safepoint(CodeCache_lock); +- if (!has_stale_entries()) { +- assert(!find_stale_entries(), "inconsistent info"); +- return; +- } +- nmethodBucket* first = dependencies(); +- nmethodBucket* last = NULL; +- int removed = 0; +- for (nmethodBucket* b = first; b != NULL;) { +- assert(b->count() >= 0, err_msg("bucket count: %d", b->count())); +- nmethodBucket* next = b->next(); +- if (b->count() == 0) { +- if (last == NULL) { +- first = next; +- } else { +- last->set_next(next); +- } +- removed++; +- delete b; +- // last stays the same. +- } else { +- last = b; +- } +- b = next; +- } +- set_dependencies(first); +- set_has_stale_entries(false); +- if (UsePerfData && removed > 0) { +- _perf_total_buckets_deallocated_count->inc(removed); +- _perf_total_buckets_stale_count->dec(removed); +- } +-} +- +-// +-// Invalidate all dependencies in the context +-int DependencyContext::remove_all_dependents() { +- assert_locked_or_safepoint(CodeCache_lock); +- nmethodBucket* b = dependencies(); +- set_dependencies(NULL); +- int marked = 0; +- int removed = 0; +- while (b != NULL) { +- nmethod* nm = b->get_nmethod(); +- if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization()) { +- nm->mark_for_deoptimization(); +- marked++; +- } +- nmethodBucket* next = b->next(); +- removed++; +- delete b; +- b = next; +- } +- set_has_stale_entries(false); +- if (UsePerfData && removed > 0) { +- _perf_total_buckets_deallocated_count->inc(removed); +- } +- return marked; +-} +- +-void DependencyContext::wipe() { +- assert_locked_or_safepoint(CodeCache_lock); +- nmethodBucket* b = dependencies(); +- set_dependencies(NULL); +- set_has_stale_entries(false); +- while (b != NULL) { +- nmethodBucket* next = b->next(); +- delete b; +- b = next; +- } +-} +- +-#ifndef PRODUCT +-void DependencyContext::print_dependent_nmethods(bool verbose) { +- int idx = 0; +- for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { +- nmethod* nm = b->get_nmethod(); +- tty->print("[%d] count=%d { ", idx++, b->count()); +- if (!verbose) { +- nm->print_on(tty, "nmethod"); +- tty->print_cr(" } "); +- } else { +- nm->print(); +- nm->print_dependencies(); +- tty->print_cr("--- } "); +- } +- } +-} +- +-bool DependencyContext::is_dependent_nmethod(nmethod* nm) { +- for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { +- if (nm == b->get_nmethod()) { +-#ifdef ASSERT +- int count = b->count(); +- assert(count >= 0, "count shouldn't be negative"); +-#endif +- return true; +- } +- } +- return false; +-} +- +-bool DependencyContext::find_stale_entries() { +- for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { +- if (b->count() == 0) return true; +- } +- return false; +-} +- +-#endif //PRODUCT +- +-int nmethodBucket::decrement() { +- return Atomic::add(-1, (volatile int *)&_count); +-} +- +-/////////////// Unit tests /////////////// +- +-#ifndef PRODUCT +- +-class TestDependencyContext { +- public: +- nmethod* _nmethods[3]; +- +- intptr_t _dependency_context; +- +- DependencyContext dependencies() { +- DependencyContext depContext(&_dependency_context); +- return depContext; +- } +- +- TestDependencyContext() : _dependency_context(DependencyContext::EMPTY) { +- CodeCache_lock->lock_without_safepoint_check(); +- +- _nmethods[0] = reinterpret_cast(0x8 * 0); +- _nmethods[1] = reinterpret_cast(0x8 * 1); +- _nmethods[2] = reinterpret_cast(0x8 * 2); +- +- dependencies().add_dependent_nmethod(_nmethods[2]); +- dependencies().add_dependent_nmethod(_nmethods[1]); +- dependencies().add_dependent_nmethod(_nmethods[0]); +- } +- +- ~TestDependencyContext() { +- dependencies().wipe(); +- CodeCache_lock->unlock(); +- } +- +- static void testRemoveDependentNmethod(int id, bool delete_immediately) { +- TestDependencyContext c; +- DependencyContext depContext = c.dependencies(); +- assert(!has_stale_entries(depContext), "check"); +- +- nmethod* nm = c._nmethods[id]; +- depContext.remove_dependent_nmethod(nm, delete_immediately); +- +- if (!delete_immediately) { +- assert(has_stale_entries(depContext), "check"); +- assert(depContext.is_dependent_nmethod(nm), "check"); +- depContext.expunge_stale_entries(); +- } +- +- assert(!has_stale_entries(depContext), "check"); +- assert(!depContext.is_dependent_nmethod(nm), "check"); +- } +- +- static void testRemoveDependentNmethod() { +- testRemoveDependentNmethod(0, false); +- testRemoveDependentNmethod(1, false); +- testRemoveDependentNmethod(2, false); +- +- testRemoveDependentNmethod(0, true); +- testRemoveDependentNmethod(1, true); +- testRemoveDependentNmethod(2, true); +- } +- +- static void test() { +- testRemoveDependentNmethod(); +- } +- +- static bool has_stale_entries(DependencyContext ctx) { +- assert(ctx.has_stale_entries() == ctx.find_stale_entries(), "check"); +- return ctx.has_stale_entries(); +- } +- +-}; +- +-void TestDependencyContext_test() { +- TestDependencyContext::test(); +-} +- +-#endif // PRODUCT +\ No newline at end of file +diff --git a/hotspot/src/share/vm/code/dependencyContext.hpp b/hotspot/src/share/vm/code/dependencyContext.hpp +deleted file mode 100644 +index 414ce0c03..000000000 +--- a/hotspot/src/share/vm/code/dependencyContext.hpp ++++ /dev/null +@@ -1,156 +0,0 @@ +-/* +- * 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. +- * +- */ +- +-#ifndef SHARE_VM_CODE_DEPENDENCYCONTEXT_HPP +-#define SHARE_VM_CODE_DEPENDENCYCONTEXT_HPP +- +-#include "memory/allocation.hpp" +-#include "oops/oop.hpp" +-#include "runtime/handles.hpp" +-#include "runtime/perfData.hpp" +- +-class nmethod; +-class DepChange; +- +-// +-// nmethodBucket is used to record dependent nmethods for +-// deoptimization. nmethod dependencies are actually +-// pairs but we really only care about the klass part for purposes of +-// finding nmethods which might need to be deoptimized. Instead of +-// recording the method, a count of how many times a particular nmethod +-// was recorded is kept. This ensures that any recording errors are +-// noticed since an nmethod should be removed as many times are it's +-// added. +-// +-class nmethodBucket: public CHeapObj { +- friend class VMStructs; +- private: +- nmethod* _nmethod; +- int _count; +- nmethodBucket* _next; +- +- public: +- nmethodBucket(nmethod* nmethod, nmethodBucket* next) : +- _nmethod(nmethod), _next(next), _count(1) {} +- +- int count() { return _count; } +- int increment() { _count += 1; return _count; } +- int decrement(); +- nmethodBucket* next() { return _next; } +- void set_next(nmethodBucket* b) { _next = b; } +- nmethod* get_nmethod() { return _nmethod; } +-}; +- +-// +-// Utility class to manipulate nmethod dependency context. +-// The context consists of nmethodBucket* (a head of a linked list) +-// and a boolean flag (does the list contains stale entries). The structure is +-// encoded as an intptr_t: lower bit is used for the flag. It is possible since +-// nmethodBucket* is aligned - the structure is malloc'ed in C heap. +-// Dependency context can be attached either to an InstanceKlass (_dep_context field) +-// or CallSiteContext oop for call_site_target dependencies (see javaClasses.hpp). +-// DependencyContext class operates on some location which holds a intptr_t value. +-// +-class DependencyContext : public StackObj { +- friend class VMStructs; +- friend class TestDependencyContext; +- private: +- enum TagBits { _has_stale_entries_bit = 1, _has_stale_entries_mask = 1 }; +- +- intptr_t* _dependency_context_addr; +- +- void set_dependencies(nmethodBucket* b) { +- assert((intptr_t(b) & _has_stale_entries_mask) == 0, "should be aligned"); +- if (has_stale_entries()) { +- *_dependency_context_addr = intptr_t(b) | _has_stale_entries_mask; +- } else { +- *_dependency_context_addr = intptr_t(b); +- } +- } +- +- void set_has_stale_entries(bool x) { +- if (x) { +- *_dependency_context_addr |= _has_stale_entries_mask; +- } else { +- *_dependency_context_addr &= ~_has_stale_entries_mask; +- } +- } +- +- nmethodBucket* dependencies() { +- intptr_t value = *_dependency_context_addr; +- return (nmethodBucket*) (value & ~_has_stale_entries_mask); +- } +- +- bool has_stale_entries() const { +- intptr_t value = *_dependency_context_addr; +- return (value & _has_stale_entries_mask) != 0; +- } +- +- static PerfCounter* _perf_total_buckets_allocated_count; +- static PerfCounter* _perf_total_buckets_deallocated_count; +- static PerfCounter* _perf_total_buckets_stale_count; +- static PerfCounter* _perf_total_buckets_stale_acc_count; +- +- public: +-#ifdef ASSERT +- // Verification for dependency contexts rooted at Java objects. +- Handle _base; // non-NULL if dependency context resides in an oop (e.g. CallSite). +- oop _base_oop; +- +- DependencyContext(intptr_t* addr, Handle base = Handle()) +- : _dependency_context_addr(addr), _base(base) +- { +- _base_oop = _base(); +- } +- +- ~DependencyContext() { +- // Base oop relocation invalidates _dependency_context_addr. +- assert(_base_oop == _base(), "base oop relocation is forbidden"); +- } +-#else +- DependencyContext(intptr_t* addr) : _dependency_context_addr(addr) {} +-#endif // ASSERT +- +- static const intptr_t EMPTY = 0; // dependencies = NULL, has_stale_entries = false +- +- static void init(); +- +- int mark_dependent_nmethods(DepChange& changes); +- void add_dependent_nmethod(nmethod* nm, bool expunge_stale_entries = false); +- void remove_dependent_nmethod(nmethod* nm, bool expunge_stale_entries = false); +- int remove_all_dependents(); +- +- void expunge_stale_entries(); +- +- // Unsafe deallocation of nmethodBuckets. Used in IK::release_C_heap_structures +- // to clean up the context possibly containing live entries pointing to unloaded nmethods. +- void wipe(); +- +-#ifndef PRODUCT +- void print_dependent_nmethods(bool verbose); +- bool is_dependent_nmethod(nmethod* nm); +- bool find_stale_entries(); +-#endif //PRODUCT +-}; +-#endif // SHARE_VM_CODE_DEPENDENCYCONTEXT_HPP +\ No newline at end of file +diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp +index 22372d074..e8f97074e 100644 +--- a/hotspot/src/share/vm/compiler/compileBroker.cpp ++++ b/hotspot/src/share/vm/compiler/compileBroker.cpp +@@ -26,7 +26,6 @@ + #include "classfile/systemDictionary.hpp" + #include "classfile/vmSymbols.hpp" + #include "code/codeCache.hpp" +-#include "code/dependencyContext.hpp" + #include "compiler/compileBroker.hpp" + #include "compiler/compileLog.hpp" + #include "compiler/compilerOracle.hpp" +@@ -920,6 +919,7 @@ void CompileBroker::compilation_init() { + PerfData::U_Ticks, CHECK); + } + ++ + if (UsePerfData) { + + EXCEPTION_MARK; +diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp +index df44e5319..ce297b681 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.cpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.cpp +@@ -28,7 +28,6 @@ + #include "classfile/systemDictionaryShared.hpp" + #include "classfile/verifier.hpp" + #include "classfile/vmSymbols.hpp" +-#include "code/dependencyContext.hpp" + #include "compiler/compileBroker.hpp" + #include "gc_implementation/shared/markSweep.inline.hpp" + #include "gc_interface/collectedHeap.inline.hpp" +@@ -195,6 +194,7 @@ InstanceKlass* InstanceKlass::allocate_instance_klass( + + int size = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size, + access_flags.is_interface(), is_anonymous); ++ + // Allocation + InstanceKlass* ik; + if (rt == REF_NONE) { +@@ -296,7 +296,7 @@ InstanceKlass::InstanceKlass(int vtable_len, + set_static_oop_field_count(0); + set_nonstatic_field_size(0); + set_is_marked_dependent(false); +- _dep_context = DependencyContext::EMPTY; ++ set_has_unloaded_dependent(false); + set_init_state(InstanceKlass::allocated); + set_init_thread(NULL); + set_init_state(allocated); +@@ -311,6 +311,7 @@ InstanceKlass::InstanceKlass(int vtable_len, + set_annotations(NULL); + set_jvmti_cached_class_field_map(NULL); + set_initial_method_idnum(0); ++ _dependencies = NULL; + set_jvmti_cached_class_field_map(NULL); + set_cached_class_file(NULL); + set_initial_method_idnum(0); +@@ -2092,31 +2093,200 @@ jmethodID InstanceKlass::jmethod_id_or_null(Method* method) { + return id; + } + +-inline DependencyContext InstanceKlass::dependencies() { +- DependencyContext dep_context(&_dep_context); +- return dep_context; ++int nmethodBucket::decrement() { ++ return Atomic::add(-1, (volatile int *)&_count); ++} ++ ++// ++// Walk the list of dependent nmethods searching for nmethods which ++// are dependent on the changes that were passed in and mark them for ++// deoptimization. Returns the number of nmethods found. ++// ++int nmethodBucket::mark_dependent_nmethods(nmethodBucket* deps, DepChange& changes) { ++ assert_locked_or_safepoint(CodeCache_lock); ++ int found = 0; ++ for (nmethodBucket* b = deps; b != NULL; b = b->next()) { ++ 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. ++ if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) { ++ if (TraceDependencies) { ++ ResourceMark rm; ++ tty->print_cr("Marked for deoptimization"); ++ changes.print(); ++ nm->print(); ++ nm->print_dependencies(); ++ } ++ nm->mark_for_deoptimization(); ++ found++; ++ } ++ } ++ return found; ++} ++ ++// ++// Add an nmethodBucket to the list of dependencies for this nmethod. ++// It's possible that an nmethod has multiple dependencies on this klass ++// so a count is kept for each bucket to guarantee that creation and ++// deletion of dependencies is consistent. Returns new head of the list. ++// ++nmethodBucket* nmethodBucket::add_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { ++ assert_locked_or_safepoint(CodeCache_lock); ++ for (nmethodBucket* b = deps; b != NULL; b = b->next()) { ++ if (nm == b->get_nmethod()) { ++ b->increment(); ++ return deps; ++ } ++ } ++ return new nmethodBucket(nm, deps); ++} ++ ++// ++// Decrement count of the nmethod in the dependency list and remove ++// the bucket completely when the count goes to 0. This method must ++// find a corresponding bucket otherwise there's a bug in the ++// recording of dependencies. Returns true if the bucket was deleted, ++// or marked ready for reclaimation. ++bool nmethodBucket::remove_dependent_nmethod(nmethodBucket** deps, nmethod* nm, bool delete_immediately) { ++ assert_locked_or_safepoint(CodeCache_lock); ++ ++ nmethodBucket* first = *deps; ++ nmethodBucket* last = NULL; ++ for (nmethodBucket* b = first; b != NULL; b = b->next()) { ++ if (nm == b->get_nmethod()) { ++ int val = b->decrement(); ++ guarantee(val >= 0, err_msg("Underflow: %d", val)); ++ if (val == 0) { ++ if (delete_immediately) { ++ if (last == NULL) { ++ *deps = b->next(); ++ } else { ++ last->set_next(b->next()); ++ } ++ delete b; ++ } ++ } ++ return true; ++ } ++ last = b; ++ } ++ ++#ifdef ASSERT ++ tty->print_raw_cr("### can't find dependent nmethod"); ++ nm->print(); ++#endif // ASSERT ++ ShouldNotReachHere(); ++ return false; ++} ++ ++// Convenience overload, for callers that don't want to delete the nmethodBucket entry. ++bool nmethodBucket::remove_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { ++ nmethodBucket** deps_addr = &deps; ++ return remove_dependent_nmethod(deps_addr, nm, false /* Don't delete */); ++} ++ ++// ++// Reclaim all unused buckets. Returns new head of the list. ++// ++nmethodBucket* nmethodBucket::clean_dependent_nmethods(nmethodBucket* deps) { ++ nmethodBucket* first = deps; ++ nmethodBucket* last = NULL; ++ nmethodBucket* b = first; ++ ++ while (b != NULL) { ++ assert(b->count() >= 0, err_msg("bucket count: %d", b->count())); ++ nmethodBucket* next = b->next(); ++ if (b->count() == 0) { ++ if (last == NULL) { ++ first = next; ++ } else { ++ last->set_next(next); ++ } ++ delete b; ++ // last stays the same. ++ } else { ++ last = b; ++ } ++ b = next; ++ } ++ return first; ++} ++ ++#ifndef PRODUCT ++void nmethodBucket::print_dependent_nmethods(nmethodBucket* deps, bool verbose) { ++ int idx = 0; ++ for (nmethodBucket* b = deps; b != NULL; b = b->next()) { ++ nmethod* nm = b->get_nmethod(); ++ tty->print("[%d] count=%d { ", idx++, b->count()); ++ if (!verbose) { ++ nm->print_on(tty, "nmethod"); ++ tty->print_cr(" } "); ++ } else { ++ nm->print(); ++ nm->print_dependencies(); ++ tty->print_cr("--- } "); ++ } ++ } + } + ++bool nmethodBucket::is_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { ++ for (nmethodBucket* b = deps; b != NULL; b = b->next()) { ++ if (nm == b->get_nmethod()) { ++#ifdef ASSERT ++ int count = b->count(); ++ assert(count >= 0, err_msg("count shouldn't be negative: %d", count)); ++#endif ++ return true; ++ } ++ } ++ return false; ++} ++#endif //PRODUCT ++ + int InstanceKlass::mark_dependent_nmethods(DepChange& changes) { +- return dependencies().mark_dependent_nmethods(changes); ++ assert_locked_or_safepoint(CodeCache_lock); ++ return nmethodBucket::mark_dependent_nmethods(_dependencies, changes); ++} ++ ++void InstanceKlass::clean_dependent_nmethods() { ++ assert_locked_or_safepoint(CodeCache_lock); ++ ++ if (has_unloaded_dependent()) { ++ _dependencies = nmethodBucket::clean_dependent_nmethods(_dependencies); ++ set_has_unloaded_dependent(false); ++ } ++#ifdef ASSERT ++ else { ++ // Verification ++ for (nmethodBucket* b = _dependencies; b != NULL; b = b->next()) { ++ assert(b->count() >= 0, err_msg("bucket count: %d", b->count())); ++ assert(b->count() != 0, "empty buckets need to be cleaned"); ++ } ++ } ++#endif + } + + void InstanceKlass::add_dependent_nmethod(nmethod* nm) { +- dependencies().add_dependent_nmethod(nm); ++ assert_locked_or_safepoint(CodeCache_lock); ++ _dependencies = nmethodBucket::add_dependent_nmethod(_dependencies, nm); + } + + void InstanceKlass::remove_dependent_nmethod(nmethod* nm, bool delete_immediately) { +- dependencies().remove_dependent_nmethod(nm, delete_immediately); ++ assert_locked_or_safepoint(CodeCache_lock); ++ ++ if (nmethodBucket::remove_dependent_nmethod(&_dependencies, nm, delete_immediately)) { ++ set_has_unloaded_dependent(true); ++ } + } + + #ifndef PRODUCT + void InstanceKlass::print_dependent_nmethods(bool verbose) { +- dependencies().print_dependent_nmethods(verbose); ++ nmethodBucket::print_dependent_nmethods(_dependencies, verbose); + } + + + bool InstanceKlass::is_dependent_nmethod(nmethod* nm) { +- return dependencies().is_dependent_nmethod(nm); ++ return nmethodBucket::is_dependent_nmethod(_dependencies, nm); + } + #endif //PRODUCT + +@@ -2413,9 +2583,7 @@ void InstanceKlass::clean_weak_instanceklass_links(BoolObjectClosure* is_alive) + clean_implementors_list(is_alive); + clean_method_data(is_alive); + +- // Since GC iterates InstanceKlasses sequentially, it is safe to remove stale entries here. +- DependencyContext dep_context(&_dep_context); +- dep_context.expunge_stale_entries(); ++ clean_dependent_nmethods(); + } + + void InstanceKlass::clean_implementors_list(BoolObjectClosure* is_alive) { +@@ -2462,8 +2630,6 @@ void InstanceKlass::remove_unshareable_info() { + + constants()->remove_unshareable_info(); + +- assert(_dep_context == DependencyContext::EMPTY, "dependency context is not shareable"); +- + for (int i = 0; i < methods()->length(); i++) { + Method* m = methods()->at(i); + m->remove_unshareable_info(); +@@ -2488,6 +2654,7 @@ void InstanceKlass::remove_unshareable_info() { + array_klasses_do(remove_unshareable_in_class); + // These are not allocated from metaspace. They are safe to set to NULL. + _member_names = NULL; ++ _dependencies = NULL; + _osr_nmethods_head = NULL; + _init_thread = NULL; + } +@@ -2628,16 +2795,14 @@ void InstanceKlass::release_C_heap_structures() { + } + } + +- // Release dependencies. +- // It is desirable to use DC::remove_all_dependents() here, but, unfortunately, +- // it is not safe (see JDK-8143408). The problem is that the klass dependency +- // context can contain live dependencies, since there's a race between nmethod & +- // klass unloading. If the klass is dead when nmethod unloading happens, relevant +- // dependencies aren't removed from the context associated with the class (see +- // nmethod::flush_dependencies). It ends up during klass unloading as seemingly +- // live dependencies pointing to unloaded nmethods and causes a crash in +- // DC::remove_all_dependents() when it touches unloaded nmethod. +- dependencies().wipe(); ++ // release dependencies ++ nmethodBucket* b = _dependencies; ++ _dependencies = NULL; ++ while (b != NULL) { ++ nmethodBucket* next = b->next(); ++ delete b; ++ b = next; ++ } + + // Deallocate breakpoint records + if (breakpoints() != 0x0) { +diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp +index 14556a38e..9750ae56d 100644 +--- a/hotspot/src/share/vm/oops/instanceKlass.hpp ++++ b/hotspot/src/share/vm/oops/instanceKlass.hpp +@@ -83,15 +83,15 @@ + + + // forward declaration for class -- see below for definition ++class SuperTypeClosure; ++class JNIid; ++class jniIdMapBase; + class BreakpointInfo; +-class DepChange; +-class DependencyContext; + class fieldDescriptor; +-class jniIdMapBase; +-class JNIid; ++class DepChange; ++class nmethodBucket; + class JvmtiCachedClassFieldMap; + class MemberNameTable; +-class SuperTypeClosure; + + // This is used in iterators below. + class FieldClosure: public StackObj { +@@ -227,6 +227,7 @@ class InstanceKlass: public Klass { + // _misc_flags. + bool _is_marked_dependent; // used for marking during flushing and deoptimization + bool _is_being_redefined; // used for locking redefinition ++ bool _has_unloaded_dependent; + + enum { + _misc_rewritten = 1 << 0, // methods rewritten. +@@ -248,7 +249,7 @@ class InstanceKlass: public Klass { + MemberNameTable* _member_names; // Member names + JNIid* _jni_ids; // First JNI identifier for static fields in this class + jmethodID* _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none +- intptr_t _dep_context; // packed DependencyContext structure ++ nmethodBucket* _dependencies; // list of dependent nmethods + nmethod* _osr_nmethods_head; // Head of list of on-stack replacement nmethods for this class + BreakpointInfo* _breakpoints; // bpt lists, managed by Method* + // Linked instanceKlasses of previous versions +@@ -479,6 +480,9 @@ class InstanceKlass: public Klass { + bool is_marked_dependent() const { return _is_marked_dependent; } + void set_is_marked_dependent(bool value) { _is_marked_dependent = value; } + ++ bool has_unloaded_dependent() const { return _has_unloaded_dependent; } ++ void set_has_unloaded_dependent(bool value) { _has_unloaded_dependent = value; } ++ + // initialization (virtuals from Klass) + bool should_be_initialized() const; // means that initialize should be called + void initialize(TRAPS); +@@ -827,8 +831,7 @@ class InstanceKlass: public Klass { + JNIid* jni_id_for(int offset); + + // maintenance of deoptimization dependencies +- inline DependencyContext dependencies(); +- int mark_dependent_nmethods(DepChange& changes); ++ int mark_dependent_nmethods(DepChange& changes); + void add_dependent_nmethod(nmethod* nm); + void remove_dependent_nmethod(nmethod* nm, bool delete_immediately); + +@@ -1023,6 +1026,7 @@ class InstanceKlass: public Klass { + void clean_weak_instanceklass_links(BoolObjectClosure* is_alive); + void clean_implementors_list(BoolObjectClosure* is_alive); + void clean_method_data(BoolObjectClosure* is_alive); ++ void clean_dependent_nmethods(); + + // Explicit metaspace deallocation of fields + // For RedefineClasses and class file parsing errors, we need to deallocate +@@ -1254,6 +1258,48 @@ class JNIid: public CHeapObj { + void verify(Klass* holder); + }; + ++ ++// ++// nmethodBucket is used to record dependent nmethods for ++// deoptimization. nmethod dependencies are actually ++// pairs but we really only care about the klass part for purposes of ++// finding nmethods which might need to be deoptimized. Instead of ++// recording the method, a count of how many times a particular nmethod ++// was recorded is kept. This ensures that any recording errors are ++// noticed since an nmethod should be removed as many times are it's ++// added. ++// ++class nmethodBucket: public CHeapObj { ++ friend class VMStructs; ++ private: ++ nmethod* _nmethod; ++ int _count; ++ nmethodBucket* _next; ++ ++ public: ++ nmethodBucket(nmethod* nmethod, nmethodBucket* next) { ++ _nmethod = nmethod; ++ _next = next; ++ _count = 1; ++ } ++ int count() { return _count; } ++ int increment() { _count += 1; return _count; } ++ int decrement(); ++ nmethodBucket* next() { return _next; } ++ void set_next(nmethodBucket* b) { _next = b; } ++ nmethod* get_nmethod() { return _nmethod; } ++ ++ static int mark_dependent_nmethods(nmethodBucket* deps, DepChange& changes); ++ static nmethodBucket* add_dependent_nmethod(nmethodBucket* deps, nmethod* nm); ++ static bool remove_dependent_nmethod(nmethodBucket** deps, nmethod* nm, bool delete_immediately); ++ static bool remove_dependent_nmethod(nmethodBucket* deps, nmethod* nm); ++ static nmethodBucket* clean_dependent_nmethods(nmethodBucket* deps); ++#ifndef PRODUCT ++ static void print_dependent_nmethods(nmethodBucket* deps, bool verbose); ++ static bool is_dependent_nmethod(nmethodBucket* deps, nmethod* nm); ++#endif //PRODUCT ++}; ++ + // An iterator that's used to access the inner classes indices in the + // InstanceKlass::_inner_classes array. + class InnerClassesIterator : public StackObj { +diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp +index de0aae9b4..ed31964ba 100644 +--- a/hotspot/src/share/vm/prims/jni.cpp ++++ b/hotspot/src/share/vm/prims/jni.cpp +@@ -5110,7 +5110,6 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args_) { + unit_test_function_call + + // Forward declaration +-void TestDependencyContext_test(); + void TestOS_test(); + void TestReservedSpace_test(); + void TestReserveMemorySpecial_test(); +@@ -5136,7 +5135,6 @@ void ChunkManager_test_list_index(); + void execute_internal_vm_tests() { + if (ExecuteInternalVMTests) { + tty->print_cr("Running internal VM tests"); +- run_unit_test(TestDependencyContext_test()); + run_unit_test(TestOS_test()); + run_unit_test(TestReservedSpace_test()); + run_unit_test(TestReserveMemorySpecial_test()); +diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp +index 231d62d29..d950d4f3f 100644 +--- a/hotspot/src/share/vm/prims/methodHandles.cpp ++++ b/hotspot/src/share/vm/prims/methodHandles.cpp +@@ -24,7 +24,6 @@ + + #include "precompiled.hpp" + #include "classfile/symbolTable.hpp" +-#include "code/dependencyContext.hpp" + #include "compiler/compileBroker.hpp" + #include "interpreter/interpreter.hpp" + #include "interpreter/oopMapCache.hpp" +@@ -947,33 +946,30 @@ int MethodHandles::find_MemberNames(KlassHandle k, + return rfill + overflow; + } + +-// Is it safe to remove stale entries from a dependency list? +-static bool safe_to_expunge() { +- // Since parallel GC threads can concurrently iterate over a dependency +- // list during safepoint, it is safe to remove entries only when +- // CodeCache lock is held. +- return CodeCache_lock->owned_by_self(); +-} +- + void MethodHandles::add_dependent_nmethod(oop call_site, nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + + oop context = java_lang_invoke_CallSite::context(call_site); +- DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); +- // Try to purge stale entries on updates. +- // Since GC doesn't clean dependency contexts rooted at CallSiteContext objects, +- // in order to avoid memory leak, stale entries are purged whenever a dependency list +- // is changed (both on addition and removal). Though memory reclamation is delayed, +- // it avoids indefinite memory usage growth. +- deps.add_dependent_nmethod(nm, /*expunge_stale_entries=*/safe_to_expunge()); ++ nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); ++ ++ nmethodBucket* new_deps = nmethodBucket::add_dependent_nmethod(deps, nm); ++ if (deps != new_deps) { ++ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps); ++ } + } + + void MethodHandles::remove_dependent_nmethod(oop call_site, nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + + oop context = java_lang_invoke_CallSite::context(call_site); +- DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); +- deps.remove_dependent_nmethod(nm, /*expunge_stale_entries=*/safe_to_expunge()); ++ nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); ++ ++ if (nmethodBucket::remove_dependent_nmethod(deps, nm)) { ++ nmethodBucket* new_deps = nmethodBucket::clean_dependent_nmethods(deps); ++ if (deps != new_deps) { ++ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps); ++ } ++ } + } + + void MethodHandles::flush_dependent_nmethods(Handle call_site, Handle target) { +@@ -982,15 +978,21 @@ void MethodHandles::flush_dependent_nmethods(Handle call_site, Handle target) { + int marked = 0; + CallSiteDepChange changes(call_site(), target()); + { +- No_Safepoint_Verifier nsv; + MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + oop context = java_lang_invoke_CallSite::context(call_site()); +- DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); +- marked = deps.mark_dependent_nmethods(changes); ++ nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); ++ ++ marked = nmethodBucket::mark_dependent_nmethods(deps, changes); ++ if (marked > 0) { ++ nmethodBucket* new_deps = nmethodBucket::clean_dependent_nmethods(deps); ++ if (deps != new_deps) { ++ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps); ++ } ++ } + } + if (marked > 0) { +- // At least one nmethod has been marked for deoptimization. ++ // At least one nmethod has been marked for deoptimization + VM_Deoptimize op; + VMThread::execute(&op); + } +@@ -1377,8 +1379,6 @@ JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobjec + } + JVM_END + +-// It is called by a Cleaner object which ensures that dropped CallSites properly +-// deallocate their dependency information. + JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) { + Handle context(THREAD, JNIHandles::resolve_non_null(context_jh)); + { +@@ -1387,11 +1387,19 @@ JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject con + + int marked = 0; + { +- No_Safepoint_Verifier nsv; + MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag); +- assert(safe_to_expunge(), "removal is not safe"); +- DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context()); +- marked = deps.remove_all_dependents(); ++ nmethodBucket* b = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context()); ++ while(b != NULL) { ++ nmethod* nm = b->get_nmethod(); ++ if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization()) { ++ nm->mark_for_deoptimization(); ++ marked++; ++ } ++ nmethodBucket* next = b->next(); ++ delete b; ++ b = next; ++ } ++ java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context(), NULL); // reset context + } + if (marked > 0) { + // At least one nmethod has been marked for deoptimization +diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp +index f709db941..b18540924 100644 +--- a/hotspot/src/share/vm/runtime/init.cpp ++++ b/hotspot/src/share/vm/runtime/init.cpp +@@ -71,7 +71,6 @@ void InlineCacheBuffer_init(); + void compilerOracle_init(); + void compilationPolicy_init(); + void compileBroker_init(); +-void dependencyContext_init(); + + // Initialization after compiler initialization + bool universe_post_init(); // must happen after compiler_init +@@ -128,7 +127,6 @@ jint init_globals() { + compilerOracle_init(); + compilationPolicy_init(); + compileBroker_init(); +- dependencyContext_init(); + VMRegImpl::set_regName(); + + if (!universe_post_init()) { +diff --git a/hotspot/src/share/vm/runtime/perfData.hpp b/hotspot/src/share/vm/runtime/perfData.hpp +index b9f5c1a79..4a62d2e0d 100644 +--- a/hotspot/src/share/vm/runtime/perfData.hpp ++++ b/hotspot/src/share/vm/runtime/perfData.hpp +@@ -424,7 +424,6 @@ class PerfLongVariant : public PerfLong { + public: + inline void inc() { (*(jlong*)_valuep)++; } + inline void inc(jlong val) { (*(jlong*)_valuep) += val; } +- inline void dec(jlong val) { inc(-val); } + inline void add(jlong val) { (*(jlong*)_valuep) += val; } + void clear_sample_helper() { _sample_helper = NULL; } + }; +diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp +index 5140c015d..744c43e02 100644 +--- a/hotspot/src/share/vm/runtime/vmStructs.cpp ++++ b/hotspot/src/share/vm/runtime/vmStructs.cpp +@@ -330,6 +330,10 @@ typedef OffsetCompactHashtable*) \ + nonstatic_field(InstanceKlass, _default_vtable_indices, Array*) \ + nonstatic_field(Klass, _super_check_offset, juint) \ +@@ -1468,6 +1472,7 @@ typedef OffsetCompactHashtable -1:1.8.0.412-b08.2 +- add Revert-Backport-8143408-8139595.patch + * Sun Apr 28 2024 Autistic_boyya -1:1.8.0.412-b08.1 - add 8256488-Use-ldpq-stpq-instead-of-ld4-st4-for-small-c.patch