diff a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp
--- a/src/hotspot/share/ci/ciEnv.cpp
+++ b/src/hotspot/share/ci/ciEnv.cpp
@@ -21,10 +21,12 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/archiveBuilder.hpp"
+#include "cds/cdsConfig.hpp"
 #include "ci/ciConstant.hpp"
 #include "ci/ciEnv.hpp"
 #include "ci/ciField.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciInstanceKlass.hpp"
@@ -38,18 +40,20 @@
 #include "classfile/systemDictionary.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "code/codeCache.hpp"
 #include "code/scopeDesc.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilationLog.hpp"
 #include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compilerEvent.hpp"
 #include "compiler/compileLog.hpp"
 #include "compiler/compileTask.hpp"
 #include "compiler/disassembler.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
+#include "gc/shared/barrierSetNMethod.hpp"
 #include "interpreter/bytecodeStream.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "jvm.h"
 #include "logging/log.hpp"
@@ -64,13 +68,15 @@
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "oops/symbolHandle.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
+#include "runtime/flags/flagSetting.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/init.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/jniHandles.inline.hpp"
 #include "runtime/reflection.hpp"
@@ -171,10 +177,12 @@
   _jvmti_can_hotswap_or_post_breakpoint = false;
   _jvmti_can_access_local_variables = false;
   _jvmti_can_post_on_exceptions = false;
   _jvmti_can_pop_frame = false;
 
+  _scc_clinit_barriers_entry = nullptr;
+
   _dyno_klasses = nullptr;
   _dyno_locs = nullptr;
   _dyno_name[0] = '\0';
 }
 
@@ -291,10 +299,12 @@
   _jvmti_can_hotswap_or_post_breakpoint = false;
   _jvmti_can_access_local_variables = false;
   _jvmti_can_post_on_exceptions = false;
   _jvmti_can_pop_frame = false;
 
+  _scc_clinit_barriers_entry = nullptr;
+
   _dyno_klasses = nullptr;
   _dyno_locs = nullptr;
 }
 
 ciEnv::~ciEnv() {
@@ -668,11 +678,11 @@
 ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
                                              int index, int obj_index,
                                              ciInstanceKlass* accessor) {
   if (obj_index >= 0) {
     ciConstant con = get_resolved_constant(cpool, obj_index);
-    if (con.is_valid()) {
+    if (con.should_be_constant()) {
       return con;
     }
   }
   constantTag tag = cpool->tag_at(index);
   if (tag.is_int()) {
@@ -869,15 +879,15 @@
 
     if (holder_is_accessible) {  // Our declared holder is loaded.
       constantTag tag = cpool->tag_ref_at(index, bc);
       assert(accessor->get_instanceKlass() == cpool->pool_holder(), "not the pool holder?");
       Method* m = lookup_method(accessor, holder, name_sym, sig_sym, bc, tag);
-      if (m != nullptr &&
-          (bc == Bytecodes::_invokestatic
-           ?  m->method_holder()->is_not_initialized()
-           : !m->method_holder()->is_loaded())) {
-        m = nullptr;
+      if (m != nullptr) {
+        ciInstanceKlass* cik = get_instance_klass(m->method_holder());
+        if ((bc == Bytecodes::_invokestatic && cik->is_not_initialized()) || !cik->is_loaded()) {
+          m = nullptr;
+        }
       }
       if (m != nullptr && ReplayCompiles && !ciReplay::is_loaded(m)) {
         m = nullptr;
       }
       if (m != nullptr) {
@@ -954,11 +964,11 @@
 // ciEnv::validate_compile_task_dependencies
 //
 // Check for changes during compilation (e.g. class loads, evolution,
 // breakpoints, call site invalidation).
 void ciEnv::validate_compile_task_dependencies(ciMethod* target) {
-  if (failing())  return;  // no need for further checks
+  assert(!failing(), "should not call this when failing");
 
   Dependencies::DepType result = dependencies()->validate_dependencies(_task);
   if (result != Dependencies::end_marker) {
     if (result == Dependencies::call_site_target_value) {
       _inc_decompile_count_on_failure = false;
@@ -981,26 +991,32 @@
                             int frame_words,
                             OopMapSet* oop_map_set,
                             ExceptionHandlerTable* handler_table,
                             ImplicitExceptionTable* inc_table,
                             AbstractCompiler* compiler,
+                            bool has_clinit_barriers,
+                            bool for_preload,
                             bool has_unsafe_access,
                             bool has_wide_vectors,
                             bool has_monitors,
                             bool has_scoped_access,
-                            int immediate_oops_patched) {
+                            int immediate_oops_patched,
+                            bool install_code,
+                            SCCEntry* scc_entry) {
   VM_ENTRY_MARK;
   nmethod* nm = nullptr;
   {
     methodHandle method(THREAD, target->get_Method());
+    bool preload = task()->preload(); // Code is preloaded before Java method execution
 
     // We require method counters to store some method state (max compilation levels) required by the compilation policy.
-    if (method->get_method_counters(THREAD) == nullptr) {
+    if (!preload && method->get_method_counters(THREAD) == nullptr) {
       record_failure("can't create method counters");
       // All buffers in the CodeBuffer are allocated in the CodeCache.
       // If the code buffer is created on each compile attempt
       // as in C2, then it must be freed.
+      // But keep shared code.
       code_buffer->free_blob();
       return;
     }
 
     // Check if memory should be freed before allocation
@@ -1013,10 +1029,20 @@
     // and invalidating our dependencies until we install this method.
     // No safepoints are allowed. Otherwise, class redefinition can occur in between.
     MutexLocker ml(Compile_lock);
     NoSafepointVerifier nsv;
 
+    if (scc_entry != nullptr) {
+      // Invalid compilation states:
+      //  - SCCache is closed, SCC entry is garbage.
+      //  - SCC entry indicates this shared code was marked invalid while it was loaded.
+      if (!SCCache::is_on() || scc_entry->not_entrant()) {
+        code_buffer->free_blob();
+        return;
+      }
+    }
+
     // Change in Jvmti state may invalidate compilation.
     if (!failing() && jvmti_state_changed()) {
       record_failure("Jvmti state change invalidated dependencies");
     }
 
@@ -1025,26 +1051,33 @@
         ( (!dtrace_method_probes() && DTraceMethodProbes) ||
           (!dtrace_alloc_probes() && DTraceAllocProbes) )) {
       record_failure("DTrace flags change invalidated dependencies");
     }
 
-    if (!failing() && target->needs_clinit_barrier() &&
+    if (!preload && !failing() && target->needs_clinit_barrier() &&
         target->holder()->is_in_error_state()) {
       record_failure("method holder is in error state");
     }
 
-    if (!failing()) {
+    if (!failing() && (scc_entry == nullptr)) {
       if (log() != nullptr) {
         // Log the dependencies which this compilation declares.
         dependencies()->log_all_dependencies();
       }
 
       // Encode the dependencies now, so we can check them right away.
       dependencies()->encode_content_bytes();
-
+    }
+    // Check for {class loads, evolution, breakpoints, ...} during compilation
+    if (!failing() && install_code) {
       // Check for {class loads, evolution, breakpoints, ...} during compilation
       validate_compile_task_dependencies(target);
+      if (failing() && preload) {
+        ResourceMark rm;
+        char *method_name = method->name_and_sig_as_C_string();
+        log_info(scc)("preload code for '%s' failed dependency check", method_name);
+      }
     }
 
     if (failing()) {
       // While not a true deoptimization, it is a preemptive decompile.
       MethodData* mdo = method()->method_data();
@@ -1060,28 +1093,62 @@
     }
 
     assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry");
     assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
 
-    nm =  nmethod::new_nmethod(method,
-                               compile_id(),
-                               entry_bci,
-                               offsets,
-                               orig_pc_offset,
-                               debug_info(), dependencies(), code_buffer,
-                               frame_words, oop_map_set,
-                               handler_table, inc_table,
-                               compiler, CompLevel(task()->comp_level()));
-
+    if (scc_entry == nullptr) {
+      scc_entry = SCCache::store_nmethod(method,
+                             compile_id(),
+                             entry_bci,
+                             offsets,
+                             orig_pc_offset,
+                             debug_info(), dependencies(), code_buffer,
+                             frame_words, oop_map_set,
+                             handler_table, inc_table,
+                             compiler,
+                             CompLevel(task()->comp_level()),
+                             has_clinit_barriers,
+                             for_preload,
+                             has_unsafe_access,
+                             has_wide_vectors,
+                             has_monitors,
+                             has_scoped_access);
+      if (scc_entry != nullptr) {
+        scc_entry->set_inlined_bytecodes(num_inlined_bytecodes());
+        if (has_clinit_barriers) {
+          set_scc_clinit_barriers_entry(scc_entry); // Record it
+          // Build second version of code without class initialization barriers
+          code_buffer->free_blob();
+          return;
+        } else if (!for_preload) {
+          SCCEntry* previous_entry = scc_clinit_barriers_entry();
+          scc_entry->set_next(previous_entry); // Link it for case of deoptimization
+        }
+      }
+    }
+    if (install_code) {
+      nm =  nmethod::new_nmethod(method,
+                                 compile_id(),
+                                 entry_bci,
+                                 offsets,
+                                 orig_pc_offset,
+                                 debug_info(), dependencies(), code_buffer,
+                                 frame_words, oop_map_set,
+                                 handler_table, inc_table,
+                                 compiler, CompLevel(task()->comp_level()),
+                                 scc_entry);
+    }
     // Free codeBlobs
     code_buffer->free_blob();
 
     if (nm != nullptr) {
       nm->set_has_unsafe_access(has_unsafe_access);
       nm->set_has_wide_vectors(has_wide_vectors);
       nm->set_has_monitors(has_monitors);
       nm->set_has_scoped_access(has_scoped_access);
+      nm->set_preloaded(preload);
+      nm->set_has_clinit_barriers(has_clinit_barriers);
       assert(!method->is_synchronized() || nm->has_monitors(), "");
 
       if (entry_bci == InvocationEntryBci) {
         if (TieredCompilation) {
           // If there is an old version we're done with it
@@ -1098,25 +1165,42 @@
 
         LogTarget(Info, nmethod, install) lt;
         if (lt.is_enabled()) {
           ResourceMark rm;
           char *method_name = method->name_and_sig_as_C_string();
-          lt.print("Installing method (%d) %s ",
-                    task()->comp_level(), method_name);
+          lt.print("Installing method (L%d) %s id=%d scc=%s%s%u",
+                    task()->comp_level(), method_name, compile_id(),
+                    task()->is_scc() ? "A" : "", preload ? "P" : "",
+                    (scc_entry != nullptr ? scc_entry->offset() : 0));
         }
         // Allow the code to be executed
         MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
         if (nm->make_in_use()) {
-          method->set_code(method, nm);
+#ifdef ASSERT
+          BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
+          if (bs_nm != nullptr && bs_nm->supports_entry_barrier(nm)) {
+            if (!bs_nm->is_armed(nm)) {
+              log_info(init)("nmethod %d %d not armed", nm->compile_id(), nm->comp_level());
+            }
+          }
+#endif // ASSERT
+          if (preload) {
+            method->set_preload_code(nm);
+          }
+          if (!preload || target->holder()->is_linked()) {
+            method->set_code(method, nm);
+          }
         }
       } else {
         LogTarget(Info, nmethod, install) lt;
         if (lt.is_enabled()) {
           ResourceMark rm;
           char *method_name = method->name_and_sig_as_C_string();
-          lt.print("Installing osr method (%d) %s @ %d",
-                    task()->comp_level(), method_name, entry_bci);
+          lt.print("Installing osr method (L%d) %s @ %d id=%u scc=%s%u",
+                   task()->comp_level(), method_name, entry_bci, compile_id(),
+                   task()->is_scc() ? "A" : "",
+                   (scc_entry != nullptr ? scc_entry->offset() : 0));
         }
         MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
         if (nm->make_in_use()) {
           method->method_holder()->add_osr_nmethod(nm);
         }
@@ -1127,13 +1211,15 @@
   NoSafepointVerifier nsv;
   if (nm != nullptr) {
     // Compilation succeeded, post what we know about it
     nm->post_compiled_method(task());
     task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
-  } else {
+  } else if (install_code) {
     // The CodeCache is full.
     record_failure("code cache is full");
+  } else {
+    task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
   }
 
   // safepoints are allowed again
 }
 
@@ -1160,10 +1246,17 @@
 
 // ------------------------------------------------------------------
 // ciEnv::notice_inlined_method()
 void ciEnv::notice_inlined_method(ciMethod* method) {
   _num_inlined_bytecodes += method->code_size_for_inlining();
+  CompileTrainingData* tdata = task()->training_data();
+  if (tdata != nullptr) {
+    GUARDED_VM_ENTRY({
+      methodHandle mh(Thread::current(), method->get_Method());
+      tdata->notice_inlined_method(task(), mh);
+    });
+  }
 }
 
 // ------------------------------------------------------------------
 // ciEnv::num_inlined_bytecodes()
 int ciEnv::num_inlined_bytecodes() const {
@@ -1684,5 +1777,76 @@
 }
 
 void ciEnv::dump_replay_data_version(outputStream* out) {
   out->print_cr("version %d", REPLAY_VERSION);
 }
+
+bool ciEnv::is_precompiled() {
+  return (task() != nullptr) && (task()->compile_reason() == CompileTask::Reason_Precompile          ||
+                                 task()->compile_reason() == CompileTask::Reason_PrecompileForPreload);
+}
+
+bool ciEnv::is_fully_initialized(InstanceKlass* ik) {
+  assert(is_precompiled(), "");
+  if (task()->method()->method_holder() == ik) {
+    return true; // FIXME: may be too strong; being_initialized, at least
+  }
+  switch (task()->compile_reason()) {
+    case CompileTask::Reason_Precompile: {
+      // check init dependencies
+      MethodTrainingData* mtd = TrainingData::lookup_for(task()->method());
+      if (mtd != nullptr) {
+        CompileTrainingData* ctd = mtd->last_toplevel_compile(task()->comp_level());
+        if (ctd != nullptr) {
+          for (int i = 0; i < ctd->init_dep_count(); i++) {
+            KlassTrainingData* ktd = ctd->init_dep(i);
+            if (ktd->has_holder() && (ktd->holder() == ik)) {
+              log_trace(precompile)("%d: init_dependency: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+              return true; // init dependency present
+            }
+          }
+        }
+      }
+      return false; // no init dependency
+    }
+    case CompileTask::Reason_PrecompileForPreload: {
+      // FIXME: assumes that all shared classes are initialized
+      if (ik->is_shared()) {
+        return true; // class init barriers
+      }
+      if (CDSConfig::is_dumping_final_static_archive() && ArchiveBuilder::is_active() &&
+          ArchiveBuilder::current()->has_been_archived((address)ik)) {
+        return true; // class init barriers
+      }
+      return false;
+    }
+    default: fatal("%s", CompileTask::reason_name(task()->compile_reason()));
+  }
+  return false;
+}
+
+InstanceKlass::ClassState ciEnv::compute_init_state_for_precompiled(InstanceKlass* ik) {
+  ASSERT_IN_VM;
+  assert(is_precompiled(), "");
+  ResourceMark rm;
+  if (is_fully_initialized(ik)) {
+    log_trace(precompile)("%d: fully_initialized: %s", task()->compile_id(), ik->external_name());
+    return InstanceKlass::ClassState::fully_initialized;
+  } else if (MetaspaceObj::is_shared(ik)) {
+    guarantee(ik->is_loaded(), ""); // FIXME: assumes pre-loading by CDS; ik->is_linked() requires pre-linking
+    log_trace(precompile)("%d: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+    return ik->init_state(); // not yet initialized
+  } else if (CDSConfig::is_dumping_final_static_archive() && ArchiveBuilder::is_active()) {
+    if (!ArchiveBuilder::current()->has_been_archived((address)ik)) {
+      fatal("New workflow: should not compile code for unarchived class: %s", ik->external_name());
+    }
+    guarantee(ik->is_loaded(), "");
+    log_trace(precompile)("%d: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+    return ik->init_state(); // not yet initialized
+  } else {
+    // Not present in the archive.
+    fatal("unloaded: %s", ik->external_name());
+//    guarantee(SystemDictionaryShared::lookup_init_state(ik) == ik->init_state(), "");
+    log_trace(precompile)("%d: allocated: %s", task()->compile_id(), ik->external_name());
+    return InstanceKlass::ClassState::allocated; // not yet linked
+  }
+}
