7177745: JSR292: Many Callsite relinkages cause target method to always run in interpreter mode
authorvlivanov
Fri, 19 Feb 2016 20:40:20 +0300
changeset 36300 5b47f168b948
parent 36299 3d75bb0d749b
child 36301 cb578d8c6cba
7177745: JSR292: Many Callsite relinkages cause target method to always run in interpreter mode Reviewed-by: jrose, kvn
hotspot/src/share/vm/code/codeCache.cpp
hotspot/src/share/vm/code/codeCache.hpp
hotspot/src/share/vm/code/dependencies.hpp
hotspot/src/share/vm/code/dependencyContext.cpp
hotspot/src/share/vm/code/nmethod.cpp
hotspot/src/share/vm/code/nmethod.hpp
hotspot/src/share/vm/oops/instanceKlass.cpp
hotspot/src/share/vm/oops/instanceKlass.hpp
hotspot/src/share/vm/runtime/vmStructs.cpp
hotspot/test/compiler/jsr292/ContinuousCallSiteTargetChange.java
--- 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;
 
--- 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
--- 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(); }
 };
--- 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++;
     }
   }
--- 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();
--- 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);
 
--- 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);
 }
 
--- 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);
 
--- 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*)                                 \
                                                                                                                                      \
--- /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();
+            }
+        }
+    }
+}