# HG changeset patch # User vlivanov # Date 1455903620 -10800 # Node ID 5b47f168b948269ebb6b16c811b5e2bda7aee0db # Parent 3d75bb0d749b5627309ab66eea2b7fca18a3212d 7177745: JSR292: Many Callsite relinkages cause target method to always run in interpreter mode Reviewed-by: jrose, kvn diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/code/codeCache.cpp --- a/hotspot/src/share/vm/code/codeCache.cpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/code/codeCache.cpp Fri Feb 19 20:40:20 2016 +0300 @@ -1023,7 +1023,7 @@ // Keeps track of time spent for checking dependencies NOT_PRODUCT(static elapsedTimer dependentCheckTime;) -int CodeCache::mark_for_deoptimization(DepChange& changes) { +int CodeCache::mark_for_deoptimization(KlassDepChange& changes) { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); int number_of_marked_CodeBlobs = 0; diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/code/codeCache.hpp --- a/hotspot/src/share/vm/code/codeCache.hpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/code/codeCache.hpp Fri Feb 19 20:40:20 2016 +0300 @@ -72,7 +72,7 @@ // Solaris and BSD. class OopClosure; -class DepChange; +class KlassDepChange; class CodeCache : AllStatic { friend class VMStructs; @@ -223,7 +223,7 @@ // Deoptimization private: - static int mark_for_deoptimization(DepChange& changes); + static int mark_for_deoptimization(KlassDepChange& changes); #ifdef HOTSWAP static int mark_for_evol_deoptimization(instanceKlassHandle dependee); #endif // HOTSWAP diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/code/dependencies.hpp --- a/hotspot/src/share/vm/code/dependencies.hpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/code/dependencies.hpp Fri Feb 19 20:40:20 2016 +0300 @@ -664,6 +664,8 @@ virtual bool is_klass_change() const { return false; } virtual bool is_call_site_change() const { return false; } + virtual void mark_for_deoptimization(nmethod* nm) = 0; + // Subclass casting with assertions. KlassDepChange* as_klass_change() { assert(is_klass_change(), "bad cast"); @@ -753,6 +755,10 @@ // What kind of DepChange is this? virtual bool is_klass_change() const { return true; } + virtual void mark_for_deoptimization(nmethod* nm) { + nm->mark_for_deoptimization(/*inc_recompile_counts=*/true); + } + Klass* new_type() { return _new_type(); } // involves_context(k) is true if k is new_type or any of the super types @@ -772,6 +778,10 @@ // What kind of DepChange is this? virtual bool is_call_site_change() const { return true; } + virtual void mark_for_deoptimization(nmethod* nm) { + nm->mark_for_deoptimization(/*inc_recompile_counts=*/false); + } + oop call_site() const { return _call_site(); } oop method_handle() const { return _method_handle(); } }; diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/code/dependencyContext.cpp --- a/hotspot/src/share/vm/code/dependencyContext.cpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/code/dependencyContext.cpp Fri Feb 19 20:40:20 2016 +0300 @@ -73,7 +73,7 @@ nm->print(); nm->print_dependencies(); } - nm->mark_for_deoptimization(); + changes.mark_for_deoptimization(nm); found++; } } diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/code/nmethod.cpp --- a/hotspot/src/share/vm/code/nmethod.cpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/code/nmethod.cpp Fri Feb 19 20:40:20 2016 +0300 @@ -536,7 +536,7 @@ _has_method_handle_invokes = 0; _lazy_critical_native = 0; _has_wide_vectors = 0; - _marked_for_deoptimization = 0; + _mark_for_deoptimization_status = not_marked; _lock_count = 0; _stack_traversal_mark = 0; _unload_reported = false; // jvmti state @@ -1459,7 +1459,7 @@ SharedRuntime::get_handle_wrong_method_stub()); } - if (is_in_use()) { + if (is_in_use() && update_recompile_counts()) { // It's a true state change, so mark the method as decompiled. // Do it only for transition from alive. inc_decompile_count(); diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/code/nmethod.hpp --- a/hotspot/src/share/vm/code/nmethod.hpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/code/nmethod.hpp Fri Feb 19 20:40:20 2016 +0300 @@ -107,6 +107,7 @@ // [Implicit Null Pointer exception table] // - implicit null table array +class DepChange; class Dependencies; class ExceptionHandlerTable; class ImplicitExceptionTable; @@ -188,7 +189,13 @@ bool _has_flushed_dependencies; // Used for maintenance of dependencies (CodeCache_lock) bool _marked_for_reclamation; // Used by NMethodSweeper (set only by sweeper) - bool _marked_for_deoptimization; // Used for stack deoptimization + + enum MarkForDeoptimizationStatus { + not_marked, + deoptimize, + deoptimize_noupdate }; + + MarkForDeoptimizationStatus _mark_for_deoptimization_status; // Used for stack deoptimization // used by jvmti to track if an unload event has been posted for this nmethod. bool _unload_reported; @@ -462,8 +469,16 @@ void set_unloading_clock(unsigned char unloading_clock); unsigned char unloading_clock(); - bool is_marked_for_deoptimization() const { return _marked_for_deoptimization; } - void mark_for_deoptimization() { _marked_for_deoptimization = true; } + bool is_marked_for_deoptimization() const { return _mark_for_deoptimization_status != not_marked; } + void mark_for_deoptimization(bool inc_recompile_counts = true) { + _mark_for_deoptimization_status = (inc_recompile_counts ? deoptimize : deoptimize_noupdate); + } + bool update_recompile_counts() const { + // Update recompile counts when either the update is explicitly requested (deoptimize) + // or the nmethod is not marked for deoptimization at all (not_marked). + // The latter happens during uncommon traps when deoptimized nmethod is made not entrant. + return _mark_for_deoptimization_status != deoptimize_noupdate; + } void make_unloaded(BoolObjectClosure* is_alive, oop cause); diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Fri Feb 19 20:40:20 2016 +0300 @@ -1879,7 +1879,7 @@ return dep_context; } -int InstanceKlass::mark_dependent_nmethods(DepChange& changes) { +int InstanceKlass::mark_dependent_nmethods(KlassDepChange& changes) { return dependencies().mark_dependent_nmethods(changes); } diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/oops/instanceKlass.hpp --- a/hotspot/src/share/vm/oops/instanceKlass.hpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Fri Feb 19 20:40:20 2016 +0300 @@ -56,7 +56,7 @@ // forward declaration for class -- see below for definition class BreakpointInfo; class ClassFileParser; -class DepChange; +class KlassDepChange; class DependencyContext; class fieldDescriptor; class jniIdMapBase; @@ -821,7 +821,7 @@ // maintenance of deoptimization dependencies inline DependencyContext dependencies(); - int mark_dependent_nmethods(DepChange& changes); + int mark_dependent_nmethods(KlassDepChange& changes); void add_dependent_nmethod(nmethod* nm); void remove_dependent_nmethod(nmethod* nm, bool delete_immediately); diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/src/share/vm/runtime/vmStructs.cpp --- a/hotspot/src/share/vm/runtime/vmStructs.cpp Fri Feb 19 10:06:19 2016 +0100 +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Fri Feb 19 20:40:20 2016 +0300 @@ -960,7 +960,6 @@ nonstatic_field(nmethod, _compile_id, int) \ nonstatic_field(nmethod, _comp_level, int) \ nonstatic_field(nmethod, _exception_cache, ExceptionCache*) \ - nonstatic_field(nmethod, _marked_for_deoptimization, bool) \ \ unchecked_c2_static_field(Deoptimization, _trap_reason_name, void*) \ \ diff -r 3d75bb0d749b -r 5b47f168b948 hotspot/test/compiler/jsr292/ContinuousCallSiteTargetChange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/jsr292/ContinuousCallSiteTargetChange.java Fri Feb 19 20:40:20 2016 +0300 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016, 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 + * @library /testlibrary + * @run main ContinuousCallSiteTargetChange + */ +import java.lang.invoke.*; +import jdk.test.lib.*; + +public class ContinuousCallSiteTargetChange { + static void testServer() throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+IgnoreUnrecognizedVMOptions", + "-server", "-XX:-TieredCompilation", "-Xbatch", + "-XX:PerBytecodeRecompilationCutoff=10", "-XX:PerMethodRecompilationCutoff=10", + "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining", + "ContinuousCallSiteTargetChange$Test", "100"); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + + analyzer.shouldHaveExitValue(0); + + analyzer.shouldNotContain("made not compilable"); + analyzer.shouldNotContain("decompile_count > PerMethodRecompilationCutoff"); + } + + static void testClient() throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+IgnoreUnrecognizedVMOptions", + "-client", "-XX:+TieredCompilation", "-XX:TieredStopAtLevel=1", "-Xbatch", + "-XX:PerBytecodeRecompilationCutoff=10", "-XX:PerMethodRecompilationCutoff=10", + "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining", + "ContinuousCallSiteTargetChange$Test", "100"); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + + analyzer.shouldHaveExitValue(0); + + analyzer.shouldNotContain("made not compilable"); + analyzer.shouldNotContain("decompile_count > PerMethodRecompilationCutoff"); + } + + public static void main(String[] args) throws Exception { + testServer(); + testClient(); + } + + static class Test { + static final MethodType mt = MethodType.methodType(void.class); + static final CallSite cs = new MutableCallSite(mt); + + static final MethodHandle mh = cs.dynamicInvoker(); + + static void f() { + } + + static void test1() throws Throwable { + mh.invokeExact(); + } + + static void test2() throws Throwable { + cs.getTarget().invokeExact(); + } + + static void iteration() throws Throwable { + MethodHandle mh1 = MethodHandles.lookup().findStatic(ContinuousCallSiteTargetChange.Test.class, "f", mt); + cs.setTarget(mh1); + for (int i = 0; i < 20_000; i++) { + test1(); + test2(); + } + } + + public static void main(String[] args) throws Throwable { + int iterations = Integer.parseInt(args[0]); + for (int i = 0; i < iterations; i++) { + iteration(); + } + } + } +}