diff a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp
--- a/src/hotspot/share/compiler/compileBroker.cpp
+++ b/src/hotspot/share/compiler/compileBroker.cpp
@@ -21,21 +21,25 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/cdsConfig.hpp"
+#include "cds/classPreloader.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "code/codeCache.hpp"
 #include "code/codeHeapState.hpp"
 #include "code/dependencyContext.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilationLog.hpp"
 #include "compiler/compilationMemoryStatistic.hpp"
 #include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/compileLog.hpp"
 #include "compiler/compilerEvent.hpp"
 #include "compiler/compilerOracle.hpp"
 #include "compiler/directivesParser.hpp"
 #include "gc/shared/memAllocator.hpp"
@@ -65,13 +69,14 @@
 #include "runtime/os.hpp"
 #include "runtime/perfData.hpp"
 #include "runtime/safepointVerifiers.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/threads.hpp"
-#include "runtime/threadSMR.hpp"
+#include "runtime/threadSMR.inline.hpp"
 #include "runtime/timerTrace.hpp"
 #include "runtime/vframe.inline.hpp"
+#include "services/management.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/events.hpp"
 #include "utilities/formatBuffer.hpp"
 #include "utilities/macros.hpp"
@@ -120,27 +125,34 @@
 #define DTRACE_METHOD_COMPILE_END_PROBE(method, comp_name, success)
 
 #endif // ndef DTRACE_ENABLED
 
 bool CompileBroker::_initialized = false;
+bool CompileBroker::_replay_initialized = false;
 volatile bool CompileBroker::_should_block = false;
 volatile int  CompileBroker::_print_compilation_warning = 0;
 volatile jint CompileBroker::_should_compile_new_jobs = run_compilation;
 
 // The installed compiler(s)
-AbstractCompiler* CompileBroker::_compilers[2];
+AbstractCompiler* CompileBroker::_compilers[3];
 
 // The maximum numbers of compiler threads to be determined during startup.
 int CompileBroker::_c1_count = 0;
 int CompileBroker::_c2_count = 0;
+int CompileBroker::_c3_count = 0;
+int CompileBroker::_sc_count = 0;
 
 // An array of compiler names as Java String objects
 jobject* CompileBroker::_compiler1_objects = nullptr;
 jobject* CompileBroker::_compiler2_objects = nullptr;
+jobject* CompileBroker::_compiler3_objects = nullptr;
+jobject* CompileBroker::_sc_objects = nullptr;
 
 CompileLog** CompileBroker::_compiler1_logs = nullptr;
 CompileLog** CompileBroker::_compiler2_logs = nullptr;
+CompileLog** CompileBroker::_compiler3_logs = nullptr;
+CompileLog** CompileBroker::_sc_logs = nullptr;
 
 // These counters are used to assign an unique ID to each compilation.
 volatile jint CompileBroker::_compilation_id     = 0;
 volatile jint CompileBroker::_osr_compilation_id = 0;
 volatile jint CompileBroker::_native_compilation_id = 0;
@@ -176,10 +188,11 @@
 elapsedTimer CompileBroker::_t_invalidated_compilation;
 elapsedTimer CompileBroker::_t_bailedout_compilation;
 
 uint CompileBroker::_total_bailout_count            = 0;
 uint CompileBroker::_total_invalidated_count        = 0;
+uint CompileBroker::_total_not_entrant_count        = 0;
 uint CompileBroker::_total_compile_count            = 0;
 uint CompileBroker::_total_osr_compile_count        = 0;
 uint CompileBroker::_total_standard_compile_count   = 0;
 uint CompileBroker::_total_compiler_stopped_count   = 0;
 uint CompileBroker::_total_compiler_restarted_count = 0;
@@ -190,13 +203,18 @@
 uint CompileBroker::_sum_nmethod_code_size          = 0;
 
 jlong CompileBroker::_peak_compilation_time        = 0;
 
 CompilerStatistics CompileBroker::_stats_per_level[CompLevel_full_optimization];
+CompilerStatistics CompileBroker::_scc_stats;
+CompilerStatistics CompileBroker::_scc_stats_per_level[CompLevel_full_optimization + 1];
 
+CompileQueue* CompileBroker::_c3_compile_queue     = nullptr;
 CompileQueue* CompileBroker::_c2_compile_queue     = nullptr;
 CompileQueue* CompileBroker::_c1_compile_queue     = nullptr;
+CompileQueue* CompileBroker::_sc1_compile_queue    = nullptr;
+CompileQueue* CompileBroker::_sc2_compile_queue    = nullptr;
 
 bool compileBroker_init() {
   if (LogEvents) {
     CompilationLog::init();
   }
@@ -223,20 +241,21 @@
 
 CompileTaskWrapper::~CompileTaskWrapper() {
   CompilerThread* thread = CompilerThread::current();
   CompileTask* task = thread->task();
   CompileLog*  log  = thread->log();
+  AbstractCompiler* comp = thread->compiler();
   if (log != nullptr && !task->is_unloaded())  task->log_task_done(log);
   thread->set_task(nullptr);
   thread->set_env(nullptr);
   if (task->is_blocking()) {
     bool free_task = false;
     {
       MutexLocker notifier(thread, task->lock());
       task->mark_complete();
 #if INCLUDE_JVMCI
-      if (CompileBroker::compiler(task->comp_level())->is_jvmci()) {
+      if (comp->is_jvmci()) {
         if (!task->has_waiter()) {
           // The waiting thread timed out and thus did not free the task.
           free_task = true;
         }
         task->set_blocking_jvmci_compile_state(nullptr);
@@ -266,10 +285,12 @@
  */
 bool CompileBroker::can_remove(CompilerThread *ct, bool do_it) {
   assert(UseDynamicNumberOfCompilerThreads, "or shouldn't be here");
   if (!ReduceNumberOfCompilerThreads) return false;
 
+  if (CompilationPolicy::have_recompilation_work()) return false;
+
   AbstractCompiler *compiler = ct->compiler();
   int compiler_count = compiler->num_compiler_threads();
   bool c1 = compiler->is_c1();
 
   // Keep at least 1 compiler thread of each type.
@@ -312,11 +333,11 @@
 
 /**
  * Add a CompileTask to a CompileQueue.
  */
 void CompileQueue::add(CompileTask* task) {
-  assert(MethodCompileQueue_lock->owned_by_self(), "must own lock");
+  assert(_lock->owned_by_self(), "must own lock");
 
   task->set_next(nullptr);
   task->set_prev(nullptr);
 
   if (_last == nullptr) {
@@ -338,30 +359,81 @@
   }
 
   // Mark the method as being in the compile queue.
   task->method()->set_queued_for_compilation();
 
+  task->mark_queued(os::elapsed_counter());
+
   if (CIPrintCompileQueue) {
     print_tty();
   }
 
   if (LogCompilation && xtty != nullptr) {
     task->log_task_queued();
   }
 
+  if (TrainingData::need_data() &&
+      !CDSConfig::is_dumping_final_static_archive()) { // FIXME: !!! MetaspaceShared::preload_and_dump() temporarily enables RecordTraining !!!
+    CompileTrainingData* tdata = CompileTrainingData::make(task);
+    if (tdata != nullptr) {
+      tdata->record_compilation_queued(task);
+      task->set_training_data(tdata);
+    }
+  }
+
   // Notify CompilerThreads that a task is available.
-  MethodCompileQueue_lock->notify_all();
+  _lock->notify_all();
+}
+
+void CompileQueue::add_pending(CompileTask* task) {
+  assert(_lock->owned_by_self() == false, "must NOT own lock");
+  assert(UseLockFreeCompileQueues, "");
+  task->method()->set_queued_for_compilation();
+  _queue.push(*task);
+  // FIXME: additional coordination needed? e.g., is it possible for compiler thread to block w/o processing pending tasks?
+  if (is_empty()) {
+    MutexLocker ml(_lock);
+    _lock->notify_all();
+  }
+}
+
+void CompileQueue::transfer_pending() {
+  assert(_lock->owned_by_self(), "must own lock");
+  while (!_queue.empty()) {
+    CompileTask* task = _queue.pop();
+//    guarantee(task->method()->queued_for_compilation(), "");
+    task->method()->set_queued_for_compilation(); // FIXME
+    if (task->method()->pending_queue_processed()) {
+      task->set_next(_first_stale);
+      task->set_prev(nullptr);
+      _first_stale = task;
+      continue; // skip
+    } else {
+      // Mark the method as being in the compile queue.
+      task->method()->set_pending_queue_processed();
+    }
+    if (CompileBroker::compilation_is_complete(task->method(), task->osr_bci(), task->comp_level(),
+                                               task->requires_online_compilation(), task->compile_reason())) {
+      task->set_next(_first_stale);
+      task->set_prev(nullptr);
+      _first_stale = task;
+      continue; // skip
+    }
+    add(task);
+  }
 }
 
 /**
  * Empties compilation queue by putting all compilation tasks onto
  * a freelist. Furthermore, the method wakes up all threads that are
  * waiting on a compilation task to finish. This can happen if background
  * compilation is disabled.
  */
 void CompileQueue::free_all() {
-  MutexLocker mu(MethodCompileQueue_lock);
+  MutexLocker mu(_lock);
+  transfer_pending();
+
   CompileTask* next = _first;
 
   // Iterate over all tasks in the compile queue
   while (next != nullptr) {
     CompileTask* current = next;
@@ -376,23 +448,27 @@
   }
   _first = nullptr;
   _last = nullptr;
 
   // Wake up all threads that block on the queue.
-  MethodCompileQueue_lock->notify_all();
+  _lock->notify_all();
 }
 
 /**
  * Get the next CompileTask from a CompileQueue
  */
 CompileTask* CompileQueue::get(CompilerThread* thread) {
   // save methods from RedefineClasses across safepoint
-  // across MethodCompileQueue_lock below.
+  // across compile queue lock below.
   methodHandle save_method;
   methodHandle save_hot_method;
 
-  MonitorLocker locker(MethodCompileQueue_lock);
+  MonitorLocker locker(_lock);
+  transfer_pending();
+
+  CompilationPolicy::sample_load_average();
+
   // If _first is null we have no more compile jobs. There are two reasons for
   // having no compile jobs: First, we compiled everything we wanted. Second,
   // we ran out of code cache so compilation has been disabled. In the latter
   // case we perform code cache sweeps to free memory such that we can re-enable
   // compilation.
@@ -417,10 +493,14 @@
     // We need a timed wait here, since compiler threads can exit if compilation
     // is disabled forever. We use 5 seconds wait time; the exiting of compiler threads
     // is not critical and we do not want idle compiler threads to wake up too often.
     locker.wait(5*1000);
 
+    transfer_pending(); // reacquired lock
+
+    if (CompilationPolicy::have_recompilation_work()) return nullptr;
+
     if (UseDynamicNumberOfCompilerThreads && _first == nullptr) {
       // Still nothing to compile. Give caller a chance to stop this thread.
       if (CompileBroker::can_remove(CompilerThread::current(), false)) return nullptr;
     }
   }
@@ -430,11 +510,11 @@
   }
 
   CompileTask* task;
   {
     NoSafepointVerifier nsv;
-    task = CompilationPolicy::select_task(this);
+    task = CompilationPolicy::select_task(this, thread);
     if (task != nullptr) {
       task = task->select_for_compilation();
     }
   }
 
@@ -452,32 +532,33 @@
 }
 
 // Clean & deallocate stale compile tasks.
 // Temporarily releases MethodCompileQueue lock.
 void CompileQueue::purge_stale_tasks() {
-  assert(MethodCompileQueue_lock->owned_by_self(), "must own lock");
+  assert(_lock->owned_by_self(), "must own lock");
   if (_first_stale != nullptr) {
     // Stale tasks are purged when MCQ lock is released,
     // but _first_stale updates are protected by MCQ lock.
     // Once task processing starts and MCQ lock is released,
     // other compiler threads can reuse _first_stale.
     CompileTask* head = _first_stale;
     _first_stale = nullptr;
     {
-      MutexUnlocker ul(MethodCompileQueue_lock);
+      MutexUnlocker ul(_lock);
       for (CompileTask* task = head; task != nullptr; ) {
         CompileTask* next_task = task->next();
         CompileTaskWrapper ctw(task); // Frees the task
         task->set_failure_reason("stale task");
         task = next_task;
       }
     }
+    transfer_pending(); // transfer pending after reacquiring MCQ lock
   }
 }
 
 void CompileQueue::remove(CompileTask* task) {
-  assert(MethodCompileQueue_lock->owned_by_self(), "must own lock");
+  assert(_lock->owned_by_self(), "must own lock");
   if (task->prev() != nullptr) {
     task->prev()->set_next(task->next());
   } else {
     // max is the first element
     assert(task == _first, "Sanity");
@@ -494,11 +575,11 @@
   --_size;
   ++_total_removed;
 }
 
 void CompileQueue::remove_and_mark_stale(CompileTask* task) {
-  assert(MethodCompileQueue_lock->owned_by_self(), "must own lock");
+  assert(_lock->owned_by_self(), "must own lock");
   remove(task);
 
   // Enqueue the task for reclamation (should be done outside MCQ lock)
   task->set_next(_first_stale);
   task->set_prev(nullptr);
@@ -514,13 +595,13 @@
     task = task->next();
   }
 }
 
 
-CompileQueue* CompileBroker::compile_queue(int comp_level) {
-  if (is_c2_compile(comp_level)) return _c2_compile_queue;
-  if (is_c1_compile(comp_level)) return _c1_compile_queue;
+CompileQueue* CompileBroker::compile_queue(int comp_level, bool is_scc) {
+  if (is_c2_compile(comp_level)) return (is_scc ? _sc2_compile_queue : _c2_compile_queue);
+  if (is_c1_compile(comp_level)) return (is_scc ? _sc1_compile_queue : _c1_compile_queue);
   return nullptr;
 }
 
 CompileQueue* CompileBroker::c1_compile_queue() {
   return _c1_compile_queue;
@@ -542,14 +623,23 @@
     _c1_compile_queue->print(st);
   }
   if (_c2_compile_queue != nullptr) {
     _c2_compile_queue->print(st);
   }
+  if (_c3_compile_queue != nullptr) {
+    _c3_compile_queue->print(st);
+  }
+  if (_sc1_compile_queue != nullptr) {
+    _sc1_compile_queue->print(st);
+  }
+  if (_sc2_compile_queue != nullptr) {
+    _sc2_compile_queue->print(st);
+  }
 }
 
 void CompileQueue::print(outputStream* st) {
-  assert_locked_or_safepoint(MethodCompileQueue_lock);
+  assert_locked_or_safepoint(_lock);
   st->print_cr("%s:", name());
   CompileTask* task = _first;
   if (task == nullptr) {
     st->print_cr("Empty");
   } else {
@@ -612,10 +702,12 @@
     return;
   }
   // Set the interface to the current compiler(s).
   _c1_count = CompilationPolicy::c1_count();
   _c2_count = CompilationPolicy::c2_count();
+  _c3_count = CompilationPolicy::c3_count();
+  _sc_count = CompilationPolicy::sc_count();
 
 #if INCLUDE_JVMCI
   if (EnableJVMCI) {
     // This is creating a JVMCICompiler singleton.
     JVMCICompiler* jvmci = new JVMCICompiler();
@@ -634,10 +726,15 @@
       } else {
 #ifdef COMPILER1
         _c1_count = JVMCIHostThreads;
 #endif // COMPILER1
       }
+#ifdef COMPILER2
+      if (SCCache::is_on() && (_c3_count > 0)) {
+        _compilers[2] = new C2Compiler();
+      }
+#endif
     }
   }
 #endif // INCLUDE_JVMCI
 
 #ifdef COMPILER1
@@ -768,13 +865,23 @@
                                           PerfData::U_None,
                                           (jlong)CompileBroker::no_compile,
                                           CHECK);
   }
 
+  log_info(scc, init)("CompileBroker is initialized");
   _initialized = true;
 }
 
+Handle CompileBroker::create_thread_oop(const char* name, TRAPS) {
+  Handle thread_oop = JavaThread::create_system_thread_object(name, CHECK_NH);
+  return thread_oop;
+}
+
+void TrainingReplayThread::training_replay_thread_entry(JavaThread* thread, TRAPS) {
+  CompilationPolicy::replay_training_at_init_loop(thread);
+}
+
 #if defined(ASSERT) && COMPILER2_OR_JVMCI
 // Stress testing. Dedicated threads revert optimizations based on escape analysis concurrently to
 // the running java application.  Configured with vm options DeoptimizeObjectsALot*.
 class DeoptimizeObjectsALotThread : public JavaThread {
 
@@ -862,10 +969,13 @@
 #if defined(ASSERT) && COMPILER2_OR_JVMCI
     case deoptimizer_t:
       new_thread = new DeoptimizeObjectsALotThread();
       break;
 #endif // ASSERT
+    case training_replay_t:
+      new_thread = new TrainingReplayThread();
+      break;
     default:
       ShouldNotReachHere();
   }
 
   // At this point the new CompilerThread data-races with this startup
@@ -941,47 +1051,69 @@
     LogStream ls(lt);
     ls.print_cr("%s", msg.as_string());
   }
 }
 
+static void print_compiler_thread(JavaThread *ct) {
+  if (trace_compiler_threads()) {
+    ResourceMark rm;
+    ThreadsListHandle tlh;  // name() depends on the TLH.
+    assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct));
+    stringStream msg;
+    msg.print("Added initial compiler thread %s", ct->name());
+    print_compiler_threads(msg);
+  }
+}
+
 void CompileBroker::init_compiler_threads() {
   // Ensure any exceptions lead to vm_exit_during_initialization.
   EXCEPTION_MARK;
 #if !defined(ZERO)
   assert(_c2_count > 0 || _c1_count > 0, "No compilers?");
 #endif // !ZERO
   // Initialize the compilation queue
   if (_c2_count > 0) {
     const char* name = JVMCI_ONLY(UseJVMCICompiler ? "JVMCI compile queue" :) "C2 compile queue";
-    _c2_compile_queue  = new CompileQueue(name);
+    _c2_compile_queue  = new CompileQueue(name, MethodCompileQueueC2_lock);
     _compiler2_objects = NEW_C_HEAP_ARRAY(jobject, _c2_count, mtCompiler);
     _compiler2_logs = NEW_C_HEAP_ARRAY(CompileLog*, _c2_count, mtCompiler);
   }
   if (_c1_count > 0) {
-    _c1_compile_queue  = new CompileQueue("C1 compile queue");
+    _c1_compile_queue  = new CompileQueue("C1 compile queue", MethodCompileQueueC1_lock);
     _compiler1_objects = NEW_C_HEAP_ARRAY(jobject, _c1_count, mtCompiler);
     _compiler1_logs = NEW_C_HEAP_ARRAY(CompileLog*, _c1_count, mtCompiler);
   }
 
+  if (_c3_count > 0) {
+    const char* name = "C2 compile queue";
+    _c3_compile_queue  = new CompileQueue(name, MethodCompileQueueC3_lock);
+    _compiler3_objects = NEW_C_HEAP_ARRAY(jobject, _c3_count, mtCompiler);
+    _compiler3_logs = NEW_C_HEAP_ARRAY(CompileLog*, _c3_count, mtCompiler);
+  }
+  if (_sc_count > 0) {
+    if (_c1_count > 0) { // C1 is present
+      _sc1_compile_queue  = new CompileQueue("C1 SC compile queue", MethodCompileQueueSC1_lock);
+    }
+    if (_c2_count > 0) { // C2 is present
+      _sc2_compile_queue  = new CompileQueue("C2 SC compile queue", MethodCompileQueueSC2_lock);
+    }
+    _sc_objects = NEW_C_HEAP_ARRAY(jobject, _sc_count, mtCompiler);
+    _sc_logs = NEW_C_HEAP_ARRAY(CompileLog*, _sc_count, mtCompiler);
+  }
+  char name_buffer[256];
+
   for (int i = 0; i < _c2_count; i++) {
     // Create a name for our thread.
     jobject thread_handle = create_compiler_thread(_compilers[1], i, CHECK);
     _compiler2_objects[i] = thread_handle;
     _compiler2_logs[i] = nullptr;
 
     if (!UseDynamicNumberOfCompilerThreads || i == 0) {
       JavaThread *ct = make_thread(compiler_t, thread_handle, _c2_compile_queue, _compilers[1], THREAD);
       assert(ct != nullptr, "should have been handled for initial thread");
       _compilers[1]->set_num_compiler_threads(i + 1);
-      if (trace_compiler_threads()) {
-        ResourceMark rm;
-        ThreadsListHandle tlh;  // name() depends on the TLH.
-        assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct));
-        stringStream msg;
-        msg.print("Added initial compiler thread %s", ct->name());
-        print_compiler_threads(msg);
-      }
+      print_compiler_thread(ct);
     }
   }
 
   for (int i = 0; i < _c1_count; i++) {
     // Create a name for our thread.
@@ -991,23 +1123,57 @@
 
     if (!UseDynamicNumberOfCompilerThreads || i == 0) {
       JavaThread *ct = make_thread(compiler_t, thread_handle, _c1_compile_queue, _compilers[0], THREAD);
       assert(ct != nullptr, "should have been handled for initial thread");
       _compilers[0]->set_num_compiler_threads(i + 1);
-      if (trace_compiler_threads()) {
-        ResourceMark rm;
-        ThreadsListHandle tlh;  // name() depends on the TLH.
-        assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct));
-        stringStream msg;
-        msg.print("Added initial compiler thread %s", ct->name());
-        print_compiler_threads(msg);
-      }
+      print_compiler_thread(ct);
+    }
+  }
+
+  for (int i = 0; i < _c3_count; i++) {
+    // Create a name for our thread.
+    os::snprintf_checked(name_buffer, sizeof(name_buffer), "C2 CompilerThread%d", i);
+    Handle thread_oop = create_thread_oop(name_buffer, CHECK);
+    jobject thread_handle = JNIHandles::make_global(thread_oop);
+    _compiler3_objects[i] = thread_handle;
+    _compiler3_logs[i] = nullptr;
+
+    JavaThread *ct = make_thread(compiler_t, thread_handle, _c3_compile_queue, _compilers[2], THREAD);
+    assert(ct != nullptr, "should have been handled for initial thread");
+    _compilers[2]->set_num_compiler_threads(i + 1);
+    print_compiler_thread(ct);
+  }
+
+  if (_sc_count > 0) {
+    int i = 0;
+    if (_c1_count > 0) { // C1 is present
+      os::snprintf_checked(name_buffer, sizeof(name_buffer), "C%d SC CompilerThread", 1);
+      Handle thread_oop = create_thread_oop(name_buffer, CHECK);
+      jobject thread_handle = JNIHandles::make_global(thread_oop);
+      _sc_objects[i] = thread_handle;
+      _sc_logs[i] = nullptr;
+      i++;
+
+      JavaThread *ct = make_thread(compiler_t, thread_handle, _sc1_compile_queue, _compilers[0], THREAD);
+      assert(ct != nullptr, "should have been handled for initial thread");
+      print_compiler_thread(ct);
+    }
+    if (_c2_count > 0) { // C2 is present
+      os::snprintf_checked(name_buffer, sizeof(name_buffer), "C%d SC CompilerThread", 2);
+      Handle thread_oop = create_thread_oop(name_buffer, CHECK);
+      jobject thread_handle = JNIHandles::make_global(thread_oop);
+      _sc_objects[i] = thread_handle;
+      _sc_logs[i] = nullptr;
+
+      JavaThread *ct = make_thread(compiler_t, thread_handle, _sc2_compile_queue, _compilers[1], THREAD);
+      assert(ct != nullptr, "should have been handled for initial thread");
+      print_compiler_thread(ct);
     }
   }
 
   if (UsePerfData) {
-    PerfDataManager::create_constant(SUN_CI, "threads", PerfData::U_Bytes, _c1_count + _c2_count, CHECK);
+    PerfDataManager::create_constant(SUN_CI, "threads", PerfData::U_Bytes, _c1_count + _c2_count + _c3_count, CHECK);
   }
 
 #if defined(ASSERT) && COMPILER2_OR_JVMCI
   if (DeoptimizeObjectsALot) {
     // Initialize and start the object deoptimizer threads
@@ -1019,10 +1185,23 @@
     }
   }
 #endif // defined(ASSERT) && COMPILER2_OR_JVMCI
 }
 
+void CompileBroker::init_training_replay() {
+  // Ensure any exceptions lead to vm_exit_during_initialization.
+  EXCEPTION_MARK;
+  if (TrainingData::have_data()) {
+    if (UseConcurrentTrainingReplay) {
+      Handle thread_oop = create_thread_oop("Training replay thread", CHECK);
+      jobject thread_handle = JNIHandles::make_local(THREAD, thread_oop());
+      make_thread(training_replay_t, thread_handle, nullptr, nullptr, THREAD);
+    }
+    _replay_initialized = true;
+  }
+}
+
 void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) {
 
   julong free_memory = os::free_memory();
   // If SegmentedCodeCache is off, both values refer to the single heap (with type CodeBlobType::All).
   size_t available_cc_np  = CodeCache::unallocated_capacity(CodeBlobType::MethodNonProfiled),
@@ -1125,16 +1304,25 @@
  */
 void CompileBroker::mark_on_stack() {
   assert(SafepointSynchronize::is_at_safepoint(), "sanity check");
   // Since we are at a safepoint, we do not need a lock to access
   // the compile queues.
+  if (_c3_compile_queue != nullptr) {
+    _c3_compile_queue->mark_on_stack();
+  }
   if (_c2_compile_queue != nullptr) {
     _c2_compile_queue->mark_on_stack();
   }
   if (_c1_compile_queue != nullptr) {
     _c1_compile_queue->mark_on_stack();
   }
+  if (_sc1_compile_queue != nullptr) {
+    _sc1_compile_queue->mark_on_stack();
+  }
+  if (_sc2_compile_queue != nullptr) {
+    _sc2_compile_queue->mark_on_stack();
+  }
 }
 
 // ------------------------------------------------------------------
 // CompileBroker::compile_method
 //
@@ -1143,17 +1331,20 @@
                                         int osr_bci,
                                         int comp_level,
                                         const methodHandle& hot_method,
                                         int hot_count,
                                         CompileTask::CompileReason compile_reason,
+                                        bool requires_online_compilation,
                                         bool blocking,
                                         Thread* thread) {
   guarantee(!method->is_abstract(), "cannot compile abstract methods");
   assert(method->method_holder()->is_instance_klass(),
          "sanity check");
-  assert(!method->method_holder()->is_not_initialized(),
-         "method holder must be initialized");
+  assert(!method->method_holder()->is_not_initialized()   ||
+         compile_reason == CompileTask::Reason_Preload    ||
+         compile_reason == CompileTask::Reason_Precompile ||
+         compile_reason == CompileTask::Reason_PrecompileForPreload, "method holder must be initialized");
   assert(!method->is_method_handle_intrinsic(), "do not enqueue these guys");
 
   if (CIPrintRequests) {
     tty->print("request: ");
     method->print_short_name(tty);
@@ -1173,11 +1364,11 @@
   }
 
   // A request has been made for compilation.  Before we do any
   // real work, check to see if the method has been compiled
   // in the meantime with a definitive result.
-  if (compilation_is_complete(method, osr_bci, comp_level)) {
+  if (compilation_is_complete(method(), osr_bci, comp_level, requires_online_compilation, compile_reason)) {
     return;
   }
 
 #ifndef PRODUCT
   if (osr_bci != -1 && !FLAG_IS_DEFAULT(OSROnlyBCI)) {
@@ -1202,19 +1393,32 @@
     return;
   }
 
   // Tiered policy requires MethodCounters to exist before adding a method to
   // the queue. Create if we don't have them yet.
-  method->get_method_counters(thread);
+  if (compile_reason != CompileTask::Reason_Preload) {
+    method->get_method_counters(thread);
+  }
+
+  SCCEntry* scc_entry = find_scc_entry(method, osr_bci, comp_level, compile_reason, requires_online_compilation);
+  bool is_scc = (scc_entry != nullptr);
 
   // Outputs from the following MutexLocker block:
-  CompileTask* task     = nullptr;
-  CompileQueue* queue  = compile_queue(comp_level);
+  CompileTask* task = nullptr;
+  CompileQueue* queue;
+#if INCLUDE_JVMCI
+  if (is_c2_compile(comp_level) && compiler2()->is_jvmci() && compiler3() != nullptr &&
+      ((JVMCICompiler*)compiler2())->force_comp_at_level_simple(method)) {
+    assert(_c3_compile_queue != nullptr, "sanity");
+    queue = _c3_compile_queue; // JVMCI compiler's methods compilation
+  } else
+#endif
+  queue = compile_queue(comp_level, is_scc);
 
   // Acquire our lock.
   {
-    MutexLocker locker(thread, MethodCompileQueue_lock);
+    ConditionalMutexLocker locker(thread, queue->lock(), !UseLockFreeCompileQueues);
 
     // Make sure the method has not slipped into the queues since
     // last we checked; note that those checks were "fast bail-outs".
     // Here we need to be more careful, see 14012000 below.
     if (compilation_is_in_queue(method)) {
@@ -1222,11 +1426,11 @@
     }
 
     // We need to check again to see if the compilation has
     // completed.  A previous compilation may have registered
     // some result.
-    if (compilation_is_complete(method, osr_bci, comp_level)) {
+    if (compilation_is_complete(method(), osr_bci, comp_level, requires_online_compilation, compile_reason)) {
       return;
     }
 
     // We now know that this compilation is not pending, complete,
     // or prohibited.  Assign a compile_id to this compilation
@@ -1312,29 +1516,65 @@
     // these bits, and their updating (here and elsewhere) under a
     // common lock.
     task = create_compile_task(queue,
                                compile_id, method,
                                osr_bci, comp_level,
-                               hot_method, hot_count, compile_reason,
-                               blocking);
+                               hot_method, hot_count, scc_entry, compile_reason,
+                               requires_online_compilation, blocking);
+
+    if (task->is_scc() && (_sc_count > 0)) {
+      // Put it on SC queue
+      queue = is_c1_compile(comp_level) ? _sc1_compile_queue : _sc2_compile_queue;
+    }
+
+    if (UseLockFreeCompileQueues) {
+      assert(queue->lock()->owned_by_self() == false, "");
+      queue->add_pending(task);
+    } else {
+      queue->add(task);
+    }
   }
 
   if (blocking) {
     wait_for_completion(task);
   }
 }
 
+SCCEntry* CompileBroker::find_scc_entry(const methodHandle& method, int osr_bci, int comp_level,
+                                        CompileTask::CompileReason compile_reason,
+                                        bool requires_online_compilation) {
+  SCCEntry* scc_entry = nullptr;
+  if (_sc_count > 0 && osr_bci == InvocationEntryBci && !requires_online_compilation && SCCache::is_on_for_read()) {
+    // Check for cached code.
+    if (compile_reason == CompileTask::Reason_Preload) {
+      scc_entry = method->scc_entry();
+      assert(scc_entry != nullptr && scc_entry->for_preload(), "sanity");
+    } else {
+      scc_entry = SCCache::find_code_entry(method, comp_level);
+    }
+  }
+  return scc_entry;
+}
+
 nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
                                        int comp_level,
                                        const methodHandle& hot_method, int hot_count,
+                                       bool requires_online_compilation,
                                        CompileTask::CompileReason compile_reason,
                                        TRAPS) {
   // Do nothing if compilebroker is not initialized or compiles are submitted on level none
   if (!_initialized || comp_level == CompLevel_none) {
     return nullptr;
   }
 
+#if INCLUDE_JVMCI
+  if (EnableJVMCI && UseJVMCICompiler &&
+      comp_level == CompLevel_full_optimization && !ClassPreloader::class_preloading_finished()) {
+    return nullptr;
+  }
+#endif
+
   AbstractCompiler *comp = CompileBroker::compiler(comp_level);
   assert(comp != nullptr, "Ensure we have a compiler");
 
 #if INCLUDE_JVMCI
   if (comp->is_jvmci() && !JVMCI::can_initialize_JVMCI()) {
@@ -1343,29 +1583,37 @@
   }
 #endif
 
   DirectiveSet* directive = DirectivesStack::getMatchingDirective(method, comp);
   // CompileBroker::compile_method can trap and can have pending async exception.
-  nmethod* nm = CompileBroker::compile_method(method, osr_bci, comp_level, hot_method, hot_count, compile_reason, directive, THREAD);
+  nmethod* nm = CompileBroker::compile_method(method, osr_bci, comp_level, hot_method, hot_count, requires_online_compilation, compile_reason, directive, THREAD);
   DirectivesStack::release(directive);
   return nm;
 }
 
 nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
                                          int comp_level,
                                          const methodHandle& hot_method, int hot_count,
+                                         bool requires_online_compilation,
                                          CompileTask::CompileReason compile_reason,
                                          DirectiveSet* directive,
                                          TRAPS) {
 
   // make sure arguments make sense
   assert(method->method_holder()->is_instance_klass(), "not an instance method");
   assert(osr_bci == InvocationEntryBci || (0 <= osr_bci && osr_bci < method->code_size()), "bci out of range");
   assert(!method->is_abstract() && (osr_bci == InvocationEntryBci || !method->is_native()), "cannot compile abstract/native methods");
-  assert(!method->method_holder()->is_not_initialized(), "method holder must be initialized");
+  assert(!method->method_holder()->is_not_initialized()   ||
+         compile_reason == CompileTask::Reason_Preload    ||
+         compile_reason == CompileTask::Reason_Precompile ||
+         compile_reason == CompileTask::Reason_PrecompileForPreload, "method holder must be initialized");
   // return quickly if possible
 
+  if (PrecompileOnlyAndExit && !CompileTask::reason_is_precompiled(compile_reason)) {
+    return nullptr;
+  }
+
   // lock, make sure that the compilation
   // isn't prohibited in a straightforward way.
   AbstractCompiler* comp = CompileBroker::compiler(comp_level);
   if (comp == nullptr || compilation_is_prohibited(method, osr_bci, comp_level, directive->ExcludeOption)) {
     return nullptr;
@@ -1373,11 +1621,11 @@
 
   if (osr_bci == InvocationEntryBci) {
     // standard compilation
     nmethod* method_code = method->code();
     if (method_code != nullptr) {
-      if (compilation_is_complete(method, osr_bci, comp_level)) {
+      if (compilation_is_complete(method(), osr_bci, comp_level, requires_online_compilation, compile_reason)) {
         return method_code;
       }
     }
     if (method->is_not_compilable(comp_level)) {
       return nullptr;
@@ -1390,11 +1638,11 @@
     if (method->is_not_osr_compilable(comp_level)) return nullptr;
   }
 
   assert(!HAS_PENDING_EXCEPTION, "No exception should be present");
   // some prerequisites that are compiler specific
-  if (comp->is_c2() || comp->is_jvmci()) {
+  if (compile_reason != CompileTask::Reason_Preload && (comp->is_c2() || comp->is_jvmci())) {
     InternalOOMEMark iom(THREAD);
     method->constants()->resolve_string_constants(CHECK_AND_CLEAR_NONASYNC_NULL);
     // Resolve all classes seen in the signature of the method
     // we are compiling.
     Method::load_signature_classes(method, CHECK_AND_CLEAR_NONASYNC_NULL);
@@ -1469,12 +1717,15 @@
     // If the compiler is shut off due to code cache getting full
     // fail out now so blocking compiles dont hang the java thread
     if (!should_compile_new_jobs()) {
       return nullptr;
     }
-    bool is_blocking = !directive->BackgroundCompilationOption || ReplayCompiles;
-    compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, compile_reason, is_blocking, THREAD);
+    bool is_blocking = ReplayCompiles                                             ||
+                       !directive->BackgroundCompilationOption                    ||
+                       (compile_reason == CompileTask::Reason_Precompile)         ||
+                       (compile_reason == CompileTask::Reason_PrecompileForPreload);
+	  compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, compile_reason, requires_online_compilation, is_blocking, THREAD);
   }
 
   // return requested nmethod
   // We accept a higher level osr method
   if (osr_bci == InvocationEntryBci) {
@@ -1486,13 +1737,19 @@
 
 // ------------------------------------------------------------------
 // CompileBroker::compilation_is_complete
 //
 // See if compilation of this method is already complete.
-bool CompileBroker::compilation_is_complete(const methodHandle& method,
-                                            int                 osr_bci,
-                                            int                 comp_level) {
+bool CompileBroker::compilation_is_complete(Method*                    method,
+                                            int                        osr_bci,
+                                            int                        comp_level,
+                                            bool                       online_only,
+                                            CompileTask::CompileReason compile_reason) {
+  if (compile_reason == CompileTask::Reason_Precompile ||
+      compile_reason == CompileTask::Reason_PrecompileForPreload) {
+    return false; // FIXME: any restrictions?
+  }
   bool is_osr = (osr_bci != standard_entry_bci);
   if (is_osr) {
     if (method->is_not_osr_compilable(comp_level)) {
       return true;
     } else {
@@ -1502,12 +1759,21 @@
   } else {
     if (method->is_not_compilable(comp_level)) {
       return true;
     } else {
       nmethod* result = method->code();
-      if (result == nullptr) return false;
-      return comp_level == result->comp_level();
+      if (result == nullptr) {
+        return false;
+      }
+      if (online_only && result->is_scc()) {
+        return false;
+      }
+      bool same_level = (comp_level == result->comp_level());
+      if (result->has_clinit_barriers()) {
+        return !same_level; // Allow replace preloaded code with new code of the same level
+      }
+      return same_level;
     }
   }
 }
 
 
@@ -1603,11 +1869,10 @@
 // ------------------------------------------------------------------
 // CompileBroker::assign_compile_id_unlocked
 //
 // Public wrapper for assign_compile_id that acquires the needed locks
 int CompileBroker::assign_compile_id_unlocked(Thread* thread, const methodHandle& method, int osr_bci) {
-  MutexLocker locker(thread, MethodCompileQueue_lock);
   return assign_compile_id(method, osr_bci);
 }
 
 // ------------------------------------------------------------------
 // CompileBroker::create_compile_task
@@ -1619,17 +1884,18 @@
                                                 const methodHandle& method,
                                                 int                 osr_bci,
                                                 int                 comp_level,
                                                 const methodHandle& hot_method,
                                                 int                 hot_count,
+                                                SCCEntry*           scc_entry,
                                                 CompileTask::CompileReason compile_reason,
+                                                bool                requires_online_compilation,
                                                 bool                blocking) {
   CompileTask* new_task = CompileTask::allocate();
   new_task->initialize(compile_id, method, osr_bci, comp_level,
-                       hot_method, hot_count, compile_reason,
-                       blocking);
-  queue->add(new_task);
+                       hot_method, hot_count, scc_entry, compile_reason, queue,
+                       requires_online_compilation, blocking);
   return new_task;
 }
 
 #if INCLUDE_JVMCI
 // The number of milliseconds to wait before checking if
@@ -1766,10 +2032,14 @@
 
     // Switch back to VM state to do compiler initialization
     ThreadInVMfromNative tv(thread);
 
     // Perform per-thread and global initializations
+    {
+      MutexLocker only_one (thread, CompileThread_lock);
+      SCCache::init_table();
+    }
     comp->initialize();
   }
 
   if (comp->is_failed()) {
     disable_compilation_forever();
@@ -1803,10 +2073,12 @@
  * out to be a problem.
  */
 void CompileBroker::shutdown_compiler_runtime(AbstractCompiler* comp, CompilerThread* thread) {
   free_buffer_blob_if_allocated(thread);
 
+  log_info(compilation)("shutdown_compiler_runtime: " INTPTR_FORMAT, p2i(thread));
+
   if (comp->should_perform_shutdown()) {
     // There are two reasons for shutting down the compiler
     // 1) compiler runtime initialization failed
     // 2) The code cache is full and the following flag is set: -XX:-UseCodeCacheFlushing
     warning("%s initialization failed. Shutting down all compilers", comp->name());
@@ -1822,10 +2094,14 @@
 
     if (_c2_compile_queue != nullptr) {
       _c2_compile_queue->free_all();
     }
 
+    if (_c3_compile_queue != nullptr) {
+      _c3_compile_queue->free_all();
+    }
+
     // Set flags so that we continue execution with using interpreter only.
     UseCompiler    = false;
     UseInterpreter = true;
 
     // We could delete compiler runtimes also. However, there are references to
@@ -1839,17 +2115,23 @@
  */
 CompileLog* CompileBroker::get_log(CompilerThread* ct) {
   if (!LogCompilation) return nullptr;
 
   AbstractCompiler *compiler = ct->compiler();
+  bool jvmci = JVMCI_ONLY( compiler->is_jvmci() ||) false;
   bool c1 = compiler->is_c1();
-  jobject* compiler_objects = c1 ? _compiler1_objects : _compiler2_objects;
+  jobject* compiler_objects = c1 ? _compiler1_objects : (_c3_count == 0 ? _compiler2_objects : (jvmci ? _compiler2_objects : _compiler3_objects));
   assert(compiler_objects != nullptr, "must be initialized at this point");
-  CompileLog** logs = c1 ? _compiler1_logs : _compiler2_logs;
+  CompileLog** logs = c1 ? _compiler1_logs : (_c3_count == 0 ? _compiler2_logs : (jvmci ? _compiler2_logs : _compiler3_logs));
   assert(logs != nullptr, "must be initialized at this point");
-  int count = c1 ? _c1_count : _c2_count;
+  int count = c1 ? _c1_count : (_c3_count == 0 ? _c2_count : (jvmci ? _c2_count : _c3_count));
 
+  if (ct->queue() == _sc1_compile_queue || ct->queue() == _sc2_compile_queue) {
+    compiler_objects = _sc_objects;
+    logs  = _sc_logs;
+    count = _sc_count;
+  }
   // Find Compiler number by its threadObj.
   oop compiler_obj = ct->threadObj();
   int compiler_number = 0;
   bool found = false;
   for (; compiler_number < count; compiler_number++) {
@@ -1923,11 +2205,14 @@
   // space in the code cache to generate the necessary stubs, etc.
   while (!is_compilation_disabled_forever()) {
     // We need this HandleMark to avoid leaking VM handles.
     HandleMark hm(thread);
 
+    CompilationPolicy::recompilation_step(RecompilationWorkUnitSize, thread);
+
     CompileTask* task = queue->get(thread);
+
     if (task == nullptr) {
       if (UseDynamicNumberOfCompilerThreads) {
         // Access compiler_count under lock to enforce consistency.
         MutexLocker only_one(CompileThread_lock);
         if (can_remove(thread, true)) {
@@ -1961,10 +2246,11 @@
           invoke_compiler_on_method(task);
           thread->start_idle_timer();
         } else {
           // After compilation is disabled, remove remaining methods from queue
           method->clear_queued_for_compilation();
+          method->set_pending_queue_processed(false);
           task->set_failure_reason("compilation is disabled");
         }
       } else {
         task->set_failure_reason("breakpoints are present");
       }
@@ -2169,10 +2455,13 @@
   bool is_osr = (osr_bci != standard_entry_bci);
   bool should_log = (thread->log() != nullptr);
   bool should_break = false;
   const int task_level = task->comp_level();
   AbstractCompiler* comp = task->compiler();
+  CompileTrainingData* tdata = task->training_data();
+  assert(tdata == nullptr || TrainingData::need_data() ||
+         CDSConfig::is_dumping_preimage_static_archive(), ""); // FIXME: MetaspaceShared::preload_and_dump() messes with RecordTraining flag
   {
     // create the handle inside it's own block so it can't
     // accidentally be referenced once the thread transitions to
     // native.  The NoHandleMark before the transition should catch
     // any cases where this occurs in the future.
@@ -2186,10 +2475,14 @@
     }
 
     DTRACE_METHOD_COMPILE_BEGIN_PROBE(method, compiler_name(task_level));
   }
 
+  if (tdata != nullptr) {
+    tdata->record_compilation_start(task);
+  }
+
   should_break = directive->BreakAtCompileOption || task->check_break_at_flags();
   if (should_log && !directive->LogOption) {
     should_log = false;
   }
 
@@ -2292,17 +2585,21 @@
     ciMethod* target = ci_env.get_method_from_handle(target_handle);
 
     TraceTime t1("compilation", &time);
     EventCompilation event;
 
+    bool install_code = true;
     if (comp == nullptr) {
       ci_env.record_method_not_compilable("no compiler");
     } else if (!ci_env.failing()) {
       if (WhiteBoxAPI && WhiteBox::compilation_locked) {
         whitebox_lock_compilation();
       }
-      comp->compile_method(&ci_env, target, osr_bci, true, directive);
+      if (StoreCachedCode && task->is_precompiled()) {
+        install_code = false; // not suitable in the current context
+      }
+      comp->compile_method(&ci_env, target, osr_bci, install_code, directive);
 
       /* Repeat compilation without installing code for profiling purposes */
       int repeat_compilation_count = directive->RepeatCompilationOption;
       while (repeat_compilation_count > 0) {
         ResourceMark rm(thread);
@@ -2312,11 +2609,11 @@
       }
     }
 
     DirectivesStack::release(directive);
 
-    if (!ci_env.failing() && !task->is_success()) {
+    if (!ci_env.failing() && !task->is_success() && install_code) {
       assert(ci_env.failure_reason() != nullptr, "expect failure reason");
       assert(false, "compiler should always document failure: %s", ci_env.failure_reason());
       // The compiler elected, without comment, not to register a result.
       // Do not attempt further compilations of this method.
       ci_env.record_method_not_compilable("compile failed");
@@ -2352,20 +2649,24 @@
         FormatBufferResource("COMPILE SKIPPED: %s",      failure_reason);
       task->print(tty, msg);
     }
   }
 
+  if (tdata != nullptr) {
+    tdata->record_compilation_end(task);
+  }
+
   methodHandle method(thread, task->method());
 
   DTRACE_METHOD_COMPILE_END_PROBE(method, compiler_name(task_level), task->is_success());
 
   collect_statistics(thread, time, task);
 
   if (PrintCompilation && PrintCompilation2) {
     tty->print("%7d ", (int) tty->time_stamp().milliseconds());  // print timestamp
     tty->print("%4d ", compile_id);    // print compilation number
-    tty->print("%s ", (is_osr ? "%" : " "));
+    tty->print("%s ", (is_osr ? "%" : (task->is_scc() ? "A" : " ")));
     if (task->is_success()) {
       tty->print("size: %d(%d) ", task->nm_total_size(), task->nm_insts_size());
     }
     tty->print_cr("time: %d inlined: %d bytes", (int)time.milliseconds(), task->num_inlined_bytecodes());
   }
@@ -2400,10 +2701,11 @@
   // compile queue lock was held. Subsequently, we acquired the compile
   // queue lock to get this task off the compile queue; thus (to belabour
   // the point somewhat) our clearing of the bits must be occurring
   // only after the setting of the bits. See also 14012000 above.
   method->clear_queued_for_compilation();
+  method->set_pending_queue_processed(false);
 }
 
 /**
  * The CodeCache is full. Print warning and disable compilation.
  * Schedule code cache cleaning so compilation can continue later.
@@ -2512,48 +2814,79 @@
   // updated regardless of the setting of the CITime and CITimeEach flags
   //
 
   // account all time, including bailouts and failures in this counter;
   // C1 and C2 counters are counting both successful and unsuccessful compiles
-  _t_total_compilation.add(time);
+  _t_total_compilation.add(&time);
 
   if (!success) {
     _total_bailout_count++;
     if (UsePerfData) {
       _perf_last_failed_method->set_value(counters->current_method());
       _perf_last_failed_type->set_value(counters->compile_type());
       _perf_total_bailout_count->inc();
     }
-    _t_bailedout_compilation.add(time);
+    _t_bailedout_compilation.add(&time);
+
+    if (CITime || log_is_enabled(Info, init)) {
+      CompilerStatistics* stats = nullptr;
+      if (task->is_scc()) {
+        int level = task->preload() ? CompLevel_full_optimization : (comp_level - 1);
+        stats = &_scc_stats_per_level[level];
+      } else {
+        stats = &_stats_per_level[comp_level-1];
+      }
+      stats->_bailout.update(time, 0);
+    }
   } else if (!task->is_success()) {
     if (UsePerfData) {
       _perf_last_invalidated_method->set_value(counters->current_method());
       _perf_last_invalidated_type->set_value(counters->compile_type());
       _perf_total_invalidated_count->inc();
     }
     _total_invalidated_count++;
-    _t_invalidated_compilation.add(time);
+    _t_invalidated_compilation.add(&time);
+
+    if (CITime || log_is_enabled(Info, init)) {
+      CompilerStatistics* stats = nullptr;
+      if (task->is_scc()) {
+        int level = task->preload() ? CompLevel_full_optimization : (comp_level - 1);
+        stats = &_scc_stats_per_level[level];
+      } else {
+        stats = &_stats_per_level[comp_level-1];
+      }
+      stats->_invalidated.update(time, 0);
+    }
   } else {
     // Compilation succeeded
 
     // update compilation ticks - used by the implementation of
     // java.lang.management.CompilationMXBean
     _perf_total_compilation->inc(time.ticks());
     _peak_compilation_time = time.milliseconds() > _peak_compilation_time ? time.milliseconds() : _peak_compilation_time;
 
-    if (CITime) {
+    if (CITime || log_is_enabled(Info, init)) {
       int bytes_compiled = method->code_size() + task->num_inlined_bytecodes();
       if (is_osr) {
-        _t_osr_compilation.add(time);
+        _t_osr_compilation.add(&time);
         _sum_osr_bytes_compiled += bytes_compiled;
       } else {
-        _t_standard_compilation.add(time);
+        _t_standard_compilation.add(&time);
         _sum_standard_bytes_compiled += method->code_size() + task->num_inlined_bytecodes();
       }
 
       // Collect statistic per compilation level
-      if (comp_level > CompLevel_none && comp_level <= CompLevel_full_optimization) {
+      if (task->is_scc()) {
+        _scc_stats._standard.update(time, bytes_compiled);
+        _scc_stats._nmethods_size += task->nm_total_size();
+        _scc_stats._nmethods_code_size += task->nm_insts_size();
+        int level = task->preload() ? CompLevel_full_optimization : (comp_level - 1);
+        CompilerStatistics* stats = &_scc_stats_per_level[level];
+        stats->_standard.update(time, bytes_compiled);
+        stats->_nmethods_size += task->nm_total_size();
+        stats->_nmethods_code_size += task->nm_insts_size();
+      } else if (comp_level > CompLevel_none && comp_level <= CompLevel_full_optimization) {
         CompilerStatistics* stats = &_stats_per_level[comp_level-1];
         if (is_osr) {
           stats->_osr.update(time, bytes_compiled);
         } else {
           stats->_standard.update(time, bytes_compiled);
@@ -2563,21 +2896,21 @@
       } else {
         assert(false, "CompilerStatistics object does not exist for compilation level %d", comp_level);
       }
 
       // Collect statistic per compiler
-      AbstractCompiler* comp = compiler(comp_level);
-      if (comp) {
+      AbstractCompiler* comp = task->compiler();
+      if (comp && !task->is_scc()) {
         CompilerStatistics* stats = comp->stats();
         if (is_osr) {
           stats->_osr.update(time, bytes_compiled);
         } else {
           stats->_standard.update(time, bytes_compiled);
         }
         stats->_nmethods_size += task->nm_total_size();
         stats->_nmethods_code_size += task->nm_insts_size();
-      } else { // if (!comp)
+      } else if (!task->is_scc()) { // if (!comp)
         assert(false, "Compiler object must exist");
       }
     }
 
     if (UsePerfData) {
@@ -2636,32 +2969,161 @@
 
 jlong CompileBroker::total_compilation_ticks() {
   return _perf_total_compilation != nullptr ? _perf_total_compilation->get_value() : 0;
 }
 
+void CompileBroker::log_not_entrant(nmethod* nm) {
+  _total_not_entrant_count++;
+  if (CITime || log_is_enabled(Info, init)) {
+    CompilerStatistics* stats = nullptr;
+    int level = nm->comp_level();
+    if (nm->is_scc()) {
+      if (nm->preloaded()) {
+        assert(level == CompLevel_full_optimization, "%d", level);
+        level = CompLevel_full_optimization + 1;
+      }
+      stats = &_scc_stats_per_level[level - 1];
+    } else {
+      stats = &_stats_per_level[level - 1];
+    }
+    stats->_made_not_entrant._count++;
+  }
+}
+
 void CompileBroker::print_times(const char* name, CompilerStatistics* stats) {
   tty->print_cr("  %s {speed: %6.3f bytes/s; standard: %6.3f s, %u bytes, %u methods; osr: %6.3f s, %u bytes, %u methods; nmethods_size: %u bytes; nmethods_code_size: %u bytes}",
                 name, stats->bytes_per_second(),
                 stats->_standard._time.seconds(), stats->_standard._bytes, stats->_standard._count,
                 stats->_osr._time.seconds(), stats->_osr._bytes, stats->_osr._count,
                 stats->_nmethods_size, stats->_nmethods_code_size);
 }
 
+static void print_helper(outputStream* st, const char* name, CompilerStatistics::Data data, bool print_time = true) {
+  if (data._count > 0) {
+    st->print("; %s: %4u methods", name, data._count);
+    if (print_time) {
+      st->print(" (in %.3fs)", data._time.seconds());
+    }
+  }
+}
+
+static void print_tier_helper(outputStream* st, const char* prefix, int tier, CompilerStatistics* stats) {
+  st->print("    %s%d: %5u methods", prefix, tier, stats->_standard._count);
+  if (stats->_standard._count > 0) {
+    st->print(" (in %.3fs)", stats->_standard._time.seconds());
+  }
+  print_helper(st, "osr",     stats->_osr);
+  print_helper(st, "bailout", stats->_bailout);
+  print_helper(st, "invalid", stats->_invalidated);
+  print_helper(st, "not_entrant", stats->_made_not_entrant, false);
+  st->cr();
+}
+
+static void print_queue_info(outputStream* st, CompileQueue* queue) {
+  if (queue != nullptr) {
+    MutexLocker ml(queue->lock());
+
+    uint  total_cnt = 0;
+    uint active_cnt = 0;
+    for (JavaThread* jt : *ThreadsSMRSupport::get_java_thread_list()) {
+      guarantee(jt != nullptr, "");
+      if (jt->is_Compiler_thread()) {
+        CompilerThread* ct = (CompilerThread*)jt;
+
+        guarantee(ct != nullptr, "");
+        if (ct->queue() == queue) {
+          ++total_cnt;
+          CompileTask* task = ct->task();
+          if (task != nullptr) {
+            ++active_cnt;
+          }
+        }
+      }
+    }
+
+    st->print("  %s (%d active / %d total threads): %u tasks",
+              queue->name(), active_cnt, total_cnt, queue->size());
+    if (queue->size() > 0) {
+      uint counts[] = {0, 0, 0, 0, 0}; // T1 ... T5
+      for (CompileTask* task = queue->first(); task != nullptr; task = task->next()) {
+        int tier = task->comp_level();
+        if (task->is_scc() && task->preload()) {
+          assert(tier == CompLevel_full_optimization, "%d", tier);
+          tier = CompLevel_full_optimization + 1;
+        }
+        counts[tier-1]++;
+      }
+      st->print(":");
+      for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level() + 1; tier++) {
+        uint cnt = counts[tier-1];
+        if (cnt > 0) {
+          st->print(" T%d: %u tasks;", tier, cnt);
+        }
+      }
+    }
+    st->cr();
+
+//    for (JavaThread* jt : *ThreadsSMRSupport::get_java_thread_list()) {
+//      guarantee(jt != nullptr, "");
+//      if (jt->is_Compiler_thread()) {
+//        CompilerThread* ct = (CompilerThread*)jt;
+//
+//        guarantee(ct != nullptr, "");
+//        if (ct->queue() == queue) {
+//          ResourceMark rm;
+//          CompileTask* task = ct->task();
+//          st->print("    %s: ", ct->name_raw());
+//          if (task != nullptr) {
+//            task->print(st, nullptr, true /*short_form*/, false /*cr*/);
+//          }
+//          st->cr();
+//        }
+//      }
+//    }
+  }
+}
+void CompileBroker::print_statistics_on(outputStream* st) {
+  st->print_cr("  Total: %u methods; %u bailouts, %u invalidated, %u non_entrant",
+               _total_compile_count, _total_bailout_count, _total_invalidated_count, _total_not_entrant_count);
+  for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level(); tier++) {
+    print_tier_helper(st, "Tier", tier, &_stats_per_level[tier-1]);
+  }
+  st->cr();
+
+  if (LoadCachedCode || StoreCachedCode) {
+    for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level() + 1; tier++) {
+      if (tier != CompLevel_full_profile) {
+        print_tier_helper(st, "SC T", tier, &_scc_stats_per_level[tier - 1]);
+      }
+    }
+    st->cr();
+  }
+
+  print_queue_info(st, _c1_compile_queue);
+  print_queue_info(st, _c2_compile_queue);
+  print_queue_info(st, _c3_compile_queue);
+  print_queue_info(st, _sc1_compile_queue);
+  print_queue_info(st, _sc2_compile_queue);
+}
+
 void CompileBroker::print_times(bool per_compiler, bool aggregate) {
   if (per_compiler) {
     if (aggregate) {
       tty->cr();
-      tty->print_cr("Individual compiler times (for compiled methods only)");
+      tty->print_cr("[%dms] Individual compiler times (for compiled methods only)", (int)tty->time_stamp().milliseconds());
       tty->print_cr("------------------------------------------------");
       tty->cr();
     }
     for (unsigned int i = 0; i < sizeof(_compilers) / sizeof(AbstractCompiler*); i++) {
       AbstractCompiler* comp = _compilers[i];
       if (comp != nullptr) {
         print_times(comp->name(), comp->stats());
       }
     }
+    if (_scc_stats._standard._count > 0) {
+      print_times("SC", &_scc_stats);
+    }
     if (aggregate) {
       tty->cr();
       tty->print_cr("Individual compilation Tier times (for compiled methods only)");
       tty->print_cr("------------------------------------------------");
       tty->cr();
@@ -2670,10 +3132,17 @@
     for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level(); tier++) {
       CompilerStatistics* stats = &_stats_per_level[tier-1];
       os::snprintf_checked(tier_name, sizeof(tier_name), "Tier%d", tier);
       print_times(tier_name, stats);
     }
+    for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level() + 1; tier++) {
+      CompilerStatistics* stats = &_scc_stats_per_level[tier-1];
+      if (stats->_standard._bytes > 0) {
+        os::snprintf_checked(tier_name, sizeof(tier_name), "SC T%d", tier);
+        print_times(tier_name, stats);
+      }
+    }
   }
 
   if (!aggregate) {
     return;
   }
@@ -2711,20 +3180,29 @@
                 osr_compile_count == 0 ? 0.0 : osr_compilation.seconds() / osr_compile_count);
   tty->print_cr("    Invalidated            : %7.3f s, Average : %2.3f s",
                 CompileBroker::_t_invalidated_compilation.seconds(),
                 total_invalidated_count == 0 ? 0.0 : CompileBroker::_t_invalidated_compilation.seconds() / total_invalidated_count);
 
+  if (StoreCachedCode || LoadCachedCode) { // Check flags because SC cache could be closed already
+    tty->cr();
+    SCCache::print_timers_on(tty);
+  }
   AbstractCompiler *comp = compiler(CompLevel_simple);
   if (comp != nullptr) {
     tty->cr();
     comp->print_timers();
   }
   comp = compiler(CompLevel_full_optimization);
   if (comp != nullptr) {
     tty->cr();
     comp->print_timers();
   }
+  comp = _compilers[2];
+  if (comp != nullptr) {
+    tty->cr();
+    comp->print_timers();
+  }
 #if INCLUDE_JVMCI
   if (EnableJVMCI) {
     JVMCICompiler *jvmci_comp = JVMCICompiler::instance(false, JavaThread::current_or_null());
     if (jvmci_comp != nullptr && jvmci_comp != comp) {
       tty->cr();
