src/hotspot/share/code/compiledIC.cpp
changeset 52857 7e268f863ff0
parent 52384 d6dc479bcdd3
child 52858 dad45affbdaa
--- a/src/hotspot/share/code/compiledIC.cpp	Wed Dec 05 17:33:01 2018 +0000
+++ b/src/hotspot/share/code/compiledIC.cpp	Wed Dec 05 15:57:26 2018 +0100
@@ -237,7 +237,13 @@
   initialize_from_iter(iter);
 }
 
-bool CompiledIC::set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecode, TRAPS) {
+// This function may fail for two reasons: either due to running out of vtable
+// stubs, or due to running out of IC stubs in an attempted transition to a
+// transitional state. The needs_ic_stub_refill value will be set if the failure
+// was due to running out of IC stubs, in which case the caller will refill IC
+// stubs and retry.
+bool CompiledIC::set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecode,
+                                    bool& needs_ic_stub_refill, TRAPS) {
   assert(CompiledICLocker::is_safe(_method), "mt unsafe call");
   assert(!is_optimized(), "cannot set an optimized virtual call to megamorphic");
   assert(is_call_to_compiled() || is_call_to_interpreted(), "going directly to megamorphic?");
@@ -259,7 +265,11 @@
     CompiledICHolder* holder = new CompiledICHolder(call_info->resolved_method()->method_holder(),
                                                     call_info->resolved_klass(), false);
     holder->claim();
-    InlineCacheBuffer::create_transition_stub(this, holder, entry);
+    if (!InlineCacheBuffer::create_transition_stub(this, holder, entry)) {
+      delete holder;
+      needs_ic_stub_refill = true;
+      return false;
+    }
   } else {
     assert(call_info->call_kind() == CallInfo::vtable_call, "either itable or vtable");
     // Can be different than selected_method->vtable_index(), due to package-private etc.
@@ -269,7 +279,10 @@
     if (entry == NULL) {
       return false;
     }
-    InlineCacheBuffer::create_transition_stub(this, NULL, entry);
+    if (!InlineCacheBuffer::create_transition_stub(this, NULL, entry)) {
+      needs_ic_stub_refill = true;
+      return false;
+    }
   }
 
   if (TraceICs) {
@@ -350,7 +363,7 @@
   return is_call_to_interpreted;
 }
 
-void CompiledIC::set_to_clean(bool in_use) {
+bool CompiledIC::set_to_clean(bool in_use) {
   assert(CompiledICLocker::is_safe(_method), "mt unsafe call");
   if (TraceInlineCacheClearing || TraceICs) {
     tty->print_cr("IC@" INTPTR_FORMAT ": set to clean", p2i(instruction_address()));
@@ -373,7 +386,9 @@
     }
   } else {
     // Unsafe transition - create stub.
-    InlineCacheBuffer::create_transition_stub(this, NULL, entry);
+    if (!InlineCacheBuffer::create_transition_stub(this, NULL, entry)) {
+      return false;
+    }
   }
   // We can't check this anymore. With lazy deopt we could have already
   // cleaned this IC entry before we even return. This is possible if
@@ -382,6 +397,7 @@
   // race because the IC entry was complete when we safepointed so
   // cleaning it immediately is harmless.
   // assert(is_clean(), "sanity check");
+  return true;
 }
 
 bool CompiledIC::is_clean() const {
@@ -393,7 +409,7 @@
   return is_clean;
 }
 
-void CompiledIC::set_to_monomorphic(CompiledICInfo& info) {
+bool CompiledIC::set_to_monomorphic(CompiledICInfo& info) {
   assert(CompiledICLocker::is_safe(_method), "mt unsafe call");
   // Updating a cache to the wrong entry can cause bugs that are very hard
   // to track down - if cache entry gets invalid - we just clean it. In
@@ -430,7 +446,11 @@
       }
     } else {
       // Call via method-klass-holder
-      InlineCacheBuffer::create_transition_stub(this, info.claim_cached_icholder(), info.entry());
+      CompiledICHolder* holder = info.claim_cached_icholder();
+      if (!InlineCacheBuffer::create_transition_stub(this, holder, info.entry())) {
+        delete holder;
+        return false;
+      }
       if (TraceICs) {
          ResourceMark rm(thread);
          tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to interpreter via icholder ", p2i(instruction_address()));
@@ -450,7 +470,9 @@
                 (!is_in_transition_state() && (info.is_optimized() || static_bound || is_clean()));
 
     if (!safe) {
-      InlineCacheBuffer::create_transition_stub(this, info.cached_metadata(), info.entry());
+      if (!InlineCacheBuffer::create_transition_stub(this, info.cached_metadata(), info.entry())) {
+        return false;
+      }
     } else {
       if (is_optimized()) {
         set_ic_destination(info.entry());
@@ -475,6 +497,7 @@
   // race because the IC entry was complete when we safepointed so
   // cleaning it immediately is harmless.
   // assert(is_call_to_compiled() || is_call_to_interpreted(), "sanity check");
+  return true;
 }
 
 
@@ -575,7 +598,7 @@
 
 // ----------------------------------------------------------------------------
 
-void CompiledStaticCall::set_to_clean(bool in_use) {
+bool CompiledStaticCall::set_to_clean(bool in_use) {
   // in_use is unused but needed to match template function in CompiledMethod
   assert(CompiledICLocker::is_safe(instruction_address()), "mt unsafe call");
   // Reset call site
@@ -585,6 +608,7 @@
   // Do not reset stub here:  It is too expensive to call find_stub.
   // Instead, rely on caller (nmethod::clear_inline_caches) to clear
   // both the call and its stub.
+  return true;
 }
 
 bool CompiledStaticCall::is_clean() const {