--- old/src/share/vm/code/dependencies.cpp 2014-01-14 12:43:57.043577316 +0100 +++ new/src/share/vm/code/dependencies.cpp 2014-01-14 12:43:56.895577319 +0100 @@ -680,6 +680,17 @@ return result; } +/** + * Returns a unique identifier for each dependency argument. + */ +uintptr_t Dependencies::DepStream::get_identifier(int i) { + if (has_oop_argument()) { + return (uintptr_t)(oopDesc*)argument_oop(i); + } else { + return (uintptr_t)argument(i); + } +} + oop Dependencies::DepStream::argument_oop(int i) { oop result = recorded_oop_at(argument_index(i)); assert(result == NULL || result->is_oop(), "must be"); @@ -715,6 +726,61 @@ return NULL; } +// ----------------- DependencySignature -------------------------------------- +bool DependencySignature::equals(const DependencySignature& sig) const { + if (type() != sig.type()) { + return false; + } + + if (args_count() != sig.args_count()) { + return false; + } + + for (int i = 0; i < sig.args_count(); i++) { + if (arg(i) != sig.arg(i)) { + return false; + } + } + return true; +} + +void* DependencySignature::operator new (size_t size) throw() { + return NEW_RESOURCE_ARRAY(DependencySignature, 1); +} + +// ----------------- DependencySignatureBuffer -------------------------------------- +DependencySignatureBuffer::DependencySignatureBuffer() { + _signatures = NEW_RESOURCE_ARRAY(GrowableArray*, Dependencies::TYPE_LIMIT); + memset(_signatures, 0, sizeof(DependencySignature*) * Dependencies::TYPE_LIMIT); +} + +/* Check if arguments are identical. Two dependency signatures are considered + * identical, if the type as well as all argument identifiers are identical. + * If the dependency has not already been checked, the dependency signature is + * added to the checked dependencies of the same type. The function returns + * false, which causes the dependency to be checked in the caller. + */ +bool DependencySignatureBuffer::add_if_missing(const DependencySignature& sig) { + const int index = sig.type(); + GrowableArray* buffer = _signatures[index]; + if (buffer == NULL) { + buffer = new GrowableArray(); + _signatures[index] = buffer; + } + + // Check if we have already checked the dependency + for (int i = 0; i < buffer->length(); i++) { + DependencySignature* checked_signature = buffer->at(i); + if (checked_signature->equals(sig)) { + return true; + } + } + + buffer->append(new DependencySignature(sig)); + return false; +} + + /// Checking dependencies: // This hierarchy walker inspects subtypes of a given type, --- old/src/share/vm/code/codeCache.cpp 2014-01-14 12:43:57.119577314 +0100 +++ new/src/share/vm/code/codeCache.cpp 2014-01-14 12:43:56.915577319 +0100 @@ -596,20 +596,13 @@ } #ifndef PRODUCT -// used to keep track of how much time is spent in mark_for_deoptimization +// Keeps track of time spent for checking dependencies static elapsedTimer dependentCheckTime; -static int dependentCheckCount = 0; -#endif // PRODUCT +#endif int CodeCache::mark_for_deoptimization(DepChange& changes) { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - -#ifndef PRODUCT - dependentCheckTime.start(); - dependentCheckCount++; -#endif // PRODUCT - int number_of_marked_CodeBlobs = 0; // search the hierarchy looking for nmethods which are affected by the loading of this class @@ -617,32 +610,23 @@ // then search the interfaces this class implements looking for nmethods // which might be dependent of the fact that an interface only had one // implementor. - - { 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); - } + // nmethod::check_all_dependencies works only correctly, if no safepoint + // can happen + 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); } +#ifndef PRODUCT if (VerifyDependencies) { - // Turn off dependency tracing while actually testing deps. - NOT_PRODUCT( FlagSetting fs(TraceDependencies, false) ); - FOR_ALL_ALIVE_NMETHODS(nm) { - if (!nm->is_marked_for_deoptimization() && - nm->check_all_dependencies()) { - ResourceMark rm; - tty->print_cr("Should have been marked for deoptimization:"); - changes.print(); - nm->print(); - nm->print_dependencies(); - } - } + // Object pointers are used as unique identifiers for dependency arguments. This + // is only possible if no safepoint, i.e., GC occurs during the verification code. + dependentCheckTime.start(); + nmethod::check_all_dependencies(changes); + dependentCheckTime.stop(); } - -#ifndef PRODUCT - dependentCheckTime.stop(); -#endif // PRODUCT +#endif return number_of_marked_CodeBlobs; } @@ -899,9 +883,7 @@ } tty->print_cr("CodeCache:"); - - tty->print_cr("nmethod dependency checking time %f", dependentCheckTime.seconds(), - dependentCheckTime.seconds() / dependentCheckCount); + tty->print_cr("nmethod dependency checking time %fs", dependentCheckTime.seconds()); if (!live.is_empty()) { live.print("live"); --- old/src/share/vm/code/nmethod.hpp 2014-01-14 12:43:57.171577313 +0100 +++ new/src/share/vm/code/nmethod.hpp 2014-01-14 12:43:56.935577318 +0100 @@ -679,7 +679,7 @@ // tells if any of this method's dependencies have been invalidated // (this is expensive!) - bool check_all_dependencies(); + static void check_all_dependencies(DepChange& changes); // tells if this compiled method is dependent on the given changes, // and the changes have invalidated it --- old/src/share/vm/code/nmethod.cpp 2014-01-14 12:43:57.375577309 +0100 +++ new/src/share/vm/code/nmethod.cpp 2014-01-14 12:43:57.175577313 +0100 @@ -2161,16 +2161,38 @@ } -bool nmethod::check_all_dependencies() { - bool found_check = false; - // wholesale check of all dependencies - for (Dependencies::DepStream deps(this); deps.next(); ) { - if (deps.check_dependency() != NULL) { - found_check = true; - NOT_DEBUG(break); +void nmethod::check_all_dependencies(DepChange& changes) { + // Checked dependencies are allocated into this ResourceMark + ResourceMark rm; + + // Turn off dependency tracing while actually testing dependencies. + NOT_PRODUCT( FlagSetting fs(TraceDependencies, false) ); + + // 'dep_signature_buffers' caches already checked dependencies. + DependencySignatureBuffer dep_signature_buffers; + + // Iterate over live nmethods and check dependencies of all nmethods that are not + // marked for deoptimization. A particular dependency is only checked once. + for(nmethod* nm = CodeCache::alive_nmethod(CodeCache::first()); nm != NULL; nm = CodeCache::alive_nmethod(CodeCache::next(nm))) { + if (!nm->is_marked_for_deoptimization()) { + for (Dependencies::DepStream deps(nm); deps.next(); ) { + // Construct abstraction of a dependency. + DependencySignature current_sig(deps); + // Determine if 'deps' is already checked. If it is not checked, + // 'add_if_missing()' adds the dependency signature and returns + // false. + if (!dep_signature_buffers.add_if_missing(current_sig)) { + if (deps.check_dependency() != NULL) { + ResourceMark rm; + tty->print_cr("Should have been marked for deoptimization:"); + changes.print(); + nm->print(); + nm->print_dependencies(); + } + } + } } } - return found_check; // tell caller if we found anything } bool nmethod::check_dependency_on(DepChange& changes) { --- old/src/share/vm/code/dependencies.hpp 2014-01-14 12:43:57.379577309 +0100 +++ new/src/share/vm/code/dependencies.hpp 2014-01-14 12:43:57.211577312 +0100 @@ -480,6 +480,9 @@ bool next(); DepType type() { return _type; } + bool has_oop_argument() { return type() == call_site_target_value; } + uintptr_t get_identifier(int i); + int argument_count() { return dep_args(type()); } int argument_index(int i) { assert(0 <= i && i < argument_count(), "oob"); return _xi[i]; } @@ -523,6 +526,47 @@ }; +class DependencySignature { + private: + int _args_count; + uintptr_t _argument_hash[Dependencies::max_arg_count]; + Dependencies::DepType _type; + + + public: + DependencySignature(DependencySignature* sig) { + _args_count = sig->args_count(); + _type = sig->type(); + for (int i = 0; i < _args_count; i++) { + _argument_hash[i] = sig->_argument_hash[i]; + } + } + + DependencySignature(Dependencies::DepStream& dep) { + _args_count = dep.argument_count(); + _type = dep.type(); + for (int i = 0; i < _args_count; i++) { + _argument_hash[i] = dep.get_identifier(i); + } + } + + bool equals(const DependencySignature& sig) const; + void* operator new(size_t size) throw(); + + int args_count() const { return _args_count; } + uintptr_t arg(int idx) const { return _argument_hash[idx]; } + Dependencies::DepType type() const { return _type; } +}; + +class DependencySignatureBuffer : public StackObj { + private: + GrowableArray** _signatures; + + public: + DependencySignatureBuffer(); + bool add_if_missing(const DependencySignature& sig); +}; + // Every particular DepChange is a sub-class of this class. class DepChange : public StackObj { public: