diff a/.jcheck/conf b/.jcheck/conf
--- a/.jcheck/conf
+++ b/.jcheck/conf
@@ -1,12 +1,12 @@
 [general]
-project=jdk
+project=leyden
 jbs=JDK
 version=24
 
 [checks]
-error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists
+error=author,committer,reviewers,merge,executable,symlink,message,hg-tag,whitespace,problemlists
 warning=issuestitle,binary
 
 [repository]
 tags=(?:jdk-(?:[1-9]([0-9]*)(?:\.(?:0|[1-9][0-9]*)){0,4})(?:\+(?:(?:[0-9]+))|(?:-ga)))|(?:jdk[4-9](?:u\d{1,3})?-(?:(?:b\d{2,3})|(?:ga)))|(?:hs\d\d(?:\.\d{1,2})?-b\d\d)
 branches=.*
@@ -21,16 +21,13 @@
 
 [checks "merge"]
 message=Merge
 
 [checks "reviewers"]
-reviewers=1
+committers=1
 ignore=duke
 
 [checks "committer"]
 role=committer
 
-[checks "issues"]
-pattern=^([124-8][0-9]{6}): (\S.*)$
-
 [checks "problemlists"]
 dirs=test/jdk|test/langtools|test/lib-test|test/hotspot/jtreg|test/jaxp
diff a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,6 +1,96 @@
-# Welcome to the JDK!
+# Welcome to the Leyden Prototype Repository!
+
+The purpose of the Leyden repository is to prototype improvements to the
+startup time, time to peak performance, and footprint of Java programs, as a part of 
+[Project Leyden](https://openjdk.org/projects/leyden). We solicit feedback from
+the Java community, with the hope that some of these improvements can be eventually
+incoporated in future JDK releases.
+
+## 0. Disclaimers
+
+- *This repository contains experimental and unstable code. It is not intended to be used
+   in a production environment.*
+- *This repository is intended for developers of the JDK, and advanced Java developers who
+   are familiar with building the JDK.*
+- *The experimental features in this repository may be changed or removed without notice.
+   Command line flags and workflows will change.*
+- *The benchmarks results reported on this page are for illustrative purposes only. Your
+   applications may get better or worse results.*
+
+## 1. Overview
+
+The Leyden "[premain](https://github.com/openjdk/leyden/blob/premain/)" prototype
+includes many optimizations that shift work from run time to earlier
+executions of the application, which are
+called _training runs_. In a training run, we pre-compute various kinds of information.
+Importantly, we pre-compile
+bytecode to native code, guided by observations of the application's actual behavior
+during the training run.
+
+The Leyden repository closely tracks the JDK main line. We are typically only a few weeks behind
+the [main-line JDK repo](https://github.com/openjdk/jdk).
+
+We have implemented the following improvements over the JDK main line:
+
+- **[Ahead-of-Time Class Loading & Linking (JEP 483)](https://openjdk.org/jeps/483)**:
+  This gives
+  the JVM the ability to put classes in the _linked_ state as soon the application starts up. As a result,
+  we can implement many other time shifting optimizations with considerably simplified assumptions.
+  - This feature is accessed with the new VM flag `-XX:+PreloadSharedClasses`.
+
+- **[Unified Ahead-of-Time Cache (JEP draft 8320264)](https://openjdk.org/jeps/8320264)**:
+  This enhancement to [CDS] is foundational to the features that follow.
+  - It enables [CDS] to store not only class metadata and heap objects (as before),
+  but also profiling data and compiled code.
+  - This feature is accessed with the new VM flag `-XX:CacheDataStore`.
+  - This option simplifies the creation of the CDS archive, and also the testing
+  of all the prototype features listed here.
+
+- **[Ahead-of-Time Method Profiling (JEP draft 8325147)](https://openjdk.org/jeps/8325147)**: We store method profiles
+  from training runs in the CDS archive, thereby enabling the JIT to begin compiling earlier during warmup.
+  As a result, Java applications can reach peak performance faster.
+  - This feature is enabled by the new VM flags `-XX:+RecordTraining` and `-XX:+ReplayTraining`.
+
+- **Ahead-of-time resolution of constant pool entries**: many
+  constant pool entries are resolved during the assembly phase. This allows the application to start up faster. Also,
+  the existence of resolved constant pool entries allows the AOT compiler to generate better code.
+  For diagnostic purposes, you can use `-XX:+UnlockDiagnosticVMOptions -XX:-AOTInvokeDynamicLinking`
+  to disable the AOT linking of constant pool entries for the `invokedynamic` bytecode.
+
+- **[Ahead-of-Time Code Compilation (JEP draft 8335368)](https://openjdk.org/jeps/8335368)**: Methods that are frequently used during the training run can be
+  compiled and stored along with the CDS archive. As a result, as soon as the application starts up
+  in the production run, its methods can be can be natively executed.
+  - This feature is enabled by the new VM flags `-XX:+StoreCachedCode`, `-XX:+LoadCachedCode`, and `-XX:CachedCodeFile`.
+  - Currently, the native code is stored in a separate file, but our plans is to eventually store the native code
+    inside the CDS archive file.
+
+- **Ahead-of-time generation of [Dynamic Proxies](https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/reflect/Proxy.html)**:
+  Dynamic proxies are frequently used by popular application frameworks. We can improve start-up time by generating these proxies ahead of time.
+  - This feature is enabled by the new VM flag `-XX:+ArchiveDynamicProxies`.
+
+- **Ahead-of-time generation of reflection data**: Reflection data (such as instances of
+  `java.lang.reflect.Method`) are generated by the JVM to support `java.lang.reflect` operations. We can
+  generate these ahead of time to improve start-up.
+  - This feature is enabled by the new VM flag `-XX:+ArchiveReflectionData`.
+
+- **Class Not Found Cache**: Sometimes application frameworks repeatedly try to load classes that do not exist. This optimization allows such failing lookups to be done quickly without repeatedly scanning the class path.
+  - This feature is enabled by the new VM flag `-XX:+ArchiveLoaderLookupCache`.
+
+The flag `-XX:CacheDataStore` automatically enables the whole bundle
+of features listed above.  This simplifies testing of the whole
+prototype.  If necessary for more detailed testing, each feature can
+be individually disabled by negating its associated flag.
+
+The names of all of these VM flags will change in a future EA build as we transition from the old “CDS” terminology to the new “AOT” terminology, as discussed [here](https://openjdk.org/jeps/483#History).
+
+[CDS]: <https://docs.oracle.com/en/java/javase/22/vm/class-data-sharing.html>
+
+## 2. Building the Leyden Repository
+
+The Leyden Repository can be built in the same way as the main-line JDK repository.
+Please use the "premain" branch. I.e., [https://github.com/openjdk/leyden/tree/premain](https://github.com/openjdk/leyden/tree/premain).
 
 For build instructions please see the
 [online documentation](https://openjdk.org/groups/build/doc/building.html),
 or either of these files:
 
@@ -8,5 +98,384 @@
 - [doc/building.md](doc/building.md) (markdown version)
 
 See <https://openjdk.org/> for more information about the OpenJDK
 Community and the JDK and see <https://bugs.openjdk.org> for JDK issue
 tracking.
+
+## 3. Trying out Leyden Features
+
+The easiest way to try out the Leyden features is to build a JVM from the Leyden repository, and use it with your application with the `-XX:CacheDataStore` flag.
+
+Here's a small benchmark that uses the JDK's built-in
+[`JavaCompiler`](https://docs.oracle.com/en/java/javase/21/docs/api/java.compiler/javax/tools/JavaCompiler.html)
+class to compile some Java source files. This benchmark spends a significant amount of start-up time 
+setting up the classes used by `JavaCompiler`, so it will benefit from the Leyden features.
+
+First, download [JavacBenchApp.java](https://github.com/iklam/jdk/blob/f95f851aed3d2bf06edabab1e7c24e15f4145d0d/test/hotspot/jtreg/runtime/cds/appcds/applications/JavacBenchApp.java)
+and compile it into a JAR file.
+
+(Remember to use the `java` program that you built from the Leyden repository.)
+
+```
+$ javac JavacBenchApp.java
+$ jar cvf JavacBenchApp.jar JavacBenchApp*.class
+added manifest
+adding: JavacBenchApp$ClassFile.class(in = 1608) (out= 787)(deflated 51%)
+adding: JavacBenchApp$FileManager.class(in = 2090) (out= 979)(deflated 53%)
+adding: JavacBenchApp$SourceFile.class(in = 1351) (out= 671)(deflated 50%)
+adding: JavacBenchApp.class(in = 7571) (out= 3302)(deflated 56%)
+```
+
+We can run this benchmark without any Leyden features. It takes 893 ms:
+
+```
+$ java -cp JavacBenchApp.jar JavacBenchApp 50
+Generated source code for 51 classes and compiled them in 893 ms
+```
+
+Now, we can perform a _training run_ and create the Leyden cache files.
+
+<b>Note: Any files `JavacBenchApp.cds*` created by previous tests must
+be deleted, before new ones are created.</b>:
+
+```
+$ rm -fv JavacBenchApp.cds*
+$ java -XX:CacheDataStore=JavacBenchApp.cds -cp JavacBenchApp.jar JavacBenchApp 50
+$ ls -l JavacBenchApp.cds*
+-r--r--r-- 1 iklam iklam 30900224 May 20 19:21 JavacBenchApp.cds
+-r--r--r-- 1 iklam iklam 16895736 May 20 19:21 JavacBenchApp.cds.code
+```
+
+Two files are created:
+
+- `JavacBenchApp.cds`: This file contains classes, heap objects and profiling data harvested from the training run.
+- `JavacBenchApp.cds.code`: This file contains AOT-compiled methods, optimized for the execution behaviors observed during the training run.
+  (Data in this file will be merged into `JavacBenchApp.cds` in a future release.)
+
+Now, we can make a _production run_ of the program with the cache files. It finishes in 423 ms, or more than twice as fast as
+before.
+
+```
+$ java -XX:CacheDataStore=JavacBenchApp.cds -cp JavacBenchApp.jar JavacBenchApp 50
+Generated source code for 51 classes and compiled them in 423 ms
+```
+
+By default, training runs end when the application terminates.  You have two other options to end training runs:
+
+- -XX:AOTEndTrainingOnMethodEntry=<method1,method2,...>[,count=100]
+- jcmd \<pid> AOT.end_training
+
+Note that AOTEndTrainingOnMethodEntry uses the same format as CompileOnly and the default count is 1
+
+### Optional VM Flags
+
+When you create the file `JavacBenchApp.cds` with the flag `-XX:CacheDataStore`,
+all of the other options described
+in the [Overview](#1-overview) section above are enabled by default. This ensures that you can get all the optimizations
+without specifying them individually.
+
+For diagnostic purposes, you can selectively disable some of the options:
+
+- The `-XX:+LoadCachedCode` and `-XX:+ReplayTraining` flags affect only the production run.
+- All other options affect only the training run.
+
+For example, you can disable the loading of the AOT code during the production run. Notice that the benchmark now
+starts more slowly than it did when AOT code was loaded.
+
+```
+$ java -XX:CacheDataStore=JavacBenchApp.cds -XX:-LoadCachedCode -cp JavacBenchApp.jar JavacBenchApp 50
+Generated source code for 51 classes and compiled them in 647 ms
+```
+
+You can also disable AOT compilation in the training run:
+
+```
+$ rm -fv JavacBenchApp.cds*
+$ java -XX:CacheDataStore=JavacBenchApp.cds -XX:-StoreCachedCode -cp JavacBenchApp.jar JavacBenchApp 50
+$ ls -l JavacBenchApp.cds*
+-r--r--r-- 1 iklam iklam 30277632 May 20 20:05 JavacBenchApp.cds
+```
+
+Note that the file `JavacBenchApp.cds.code` is no longer created.
+
+## 4. Limitations of the Leyden Prototype
+
+When trying out the Leyden, please pay attention to the following limitations.
+
+### The Same Garbage Collector Must be Used between Training and Production Runs
+
+The CDS archive generated by the Leyden prototype includes machine instructions that are specific to
+the garbage collector. We recommend that you explicitly specify the same collector during both
+training and production runs. For example:
+
+```
+# training run
+$ rm -fv JavacBenchApp.cds*
+$ java -XX:CacheDataStore=JavacBenchApp.cds -XX:+UseSerialGC -cp JavacBenchApp.jar JavacBenchApp 50
+
+# production run
+$ java -XX:CacheDataStore=JavacBenchApp.cds -XX:+UseSerialGC -cp JavacBenchApp.jar JavacBenchApp 50
+```
+
+Otherwise, the CDS archive may not be loaded for the production run, leading to suboptimal performance.
+For example, sometimes you may perform the training run on a large development host, and then use
+a container to run the application in a small production node. In the following scenario, as the collector
+is not explicitly specified, the VM will automatically pick G1 for the training run, and SerialGC for the
+production run (due to its limited amount of memory):
+
+```
+# training run (uses G1 by default)
+$ rm -fv JavacBenchApp.cds*
+$ java -XX:CacheDataStore=JavacBenchApp.cds -cp JavacBenchApp.jar JavacBenchApp 50
+
+# production run (uses SerialGC)
+$ docker run --rm -v /repos/leyden/build/linux-x64/images/jdk:/jdk -v $(pwd):/test \
+    --memory=1024m \
+    container-registry.oracle.com/java/openjdk \
+    bash -c 'cd /test; /jdk/bin/java -XX:CacheDataStore=JavacBenchApp.cds -cp JavacBenchApp.jar JavacBenchApp 50'
+[0.001s][error][cds] CDS archive has preloaded classes. It cannot be used because GC used during dump time (G1)
+                     is not the same as runtime (Serial)
+[0.001s][error][cds] An error has occurred while processing the shared archive file.
+[0.001s][error][cds] Unable to map shared spaces
+Error occurred during initialization of VM
+Unable to use shared archive.
+```
+### Only G1GC, SerialGC, ParallelGC, EpsilonGC, ShenandoahGC are Supported
+=======
+
+Currently, if you use any other garbage collector in combination with `-XX:CacheDataStore`, the VM will
+exit with an error.
+
+```
+$ java -XX:+UseZGC -XX:CacheDataStore=foo --version
+Error occurred during initialization of VM
+Cannot create the CacheDataStore: UseCompressedClassPointers must be enabled, and collector
+must be G1, Parallel, Serial, Epsilon, or Shenandoah
+```
+
+
+### -Xshare:on is Enabled by default
+
+As seen in the example immediately above, in the production run, if the CDS archive cannot be
+used for any reason, the JVM will report an error and exit. This happens as if `-Xshare:on` was
+specified in the command-line.
+
+In the standard JDK, when the CDS archive cannot be used for any reason (for example, the
+archive was created for a different version of the JDK), the application will
+continue to run without using CDS.
+This fall-back strategy ensures that the application will function correctly, though at a lower level of performance.
+
+With the Leyden prototype, we have changed this fall-back behavior to make it easier to diagnose
+performance issues. For example, when the start-up time is not as good as one would expect, we
+want know whether it's caused by a misconfiguration that prevents the CDS archive
+from being used, or it's caused by a deficiency in the implementation of the Leyden optimizations.
+
+To revert to the behavior of the standard JDK, you can explicitly add `-Xshare:auto` to the command-line.
+
+```
+$ docker run --rm -v /repos/leyden/build/linux-x64/images/jdk:/jdk -v $(pwd):/test \
+    --memory=1024m \
+    container-registry.oracle.com/java/openjdk \
+    bash -c 'cd /test; /jdk/bin/java -Xshare:auto -XX:CacheDataStore=JavacBenchApp.cds -cp JavacBenchApp.jar JavacBenchApp 50'
+[0.001s][error][cds] CDS archive has preloaded classes. It cannot be used because GC used during dump time (G1)
+                     is not the same as runtime (Serial)
+Generated source code for 51 classes and compiled them in 831 ms
+```
+
+See [here](https://docs.oracle.com/en/java/javase/21/vm/class-data-sharing.html) for a discussion of `-Xshare:on` vs  `-Xshare:auto`.
+
+
+## 5. Benchmarking
+
+We use a small set of benchmarks to demonstrate the performance of the optimizations in the Leyden repo.
+
+| Benchmark  | Source |
+| ------------- | ------------- |
+|[helidon-quickstart-se](test/hotspot/jtreg/premain/helidon-quickstart-se) | https://helidon.io/docs/v4/se/guides/quickstart|
+|[micronaut-first-app](test/hotspot/jtreg/premain/micronaut-first-app) | https://guides.micronaut.io/latest/creating-your-first-micronaut-app-maven-java.html|
+|[quarkus-getting-started](test/hotspot/jtreg/premain/quarkus-getting-started) | https://quarkus.io/guides/getting-started|
+|[spring-boot-getting-started](test/hotspot/jtreg/premain/spring-boot-getting-started) | https://spring.io/guides/gs/spring-boot|
+|[spring-petclinic](test/hotspot/jtreg/premain/spring-petclinic) | https://github.com/spring-projects/spring-petclinic|
+
+*(FIXME: add a benchmark for javac)*
+
+### Benchmarking Against JDK Main-line
+
+To can compare the performance of Leyden vs the main-line JDK, you need:
+
+- An official build of JDK 21
+- An up-to-date build of the JDK main-line
+- The latest Leyden build
+- Maven (ideally 3.8 or later, as required by some of the demos). Note: if you are behind
+  a firewall, you may need to [set up proxies for Maven](https://maven.apache.org/guides/mini/guide-proxies.html)
+
+The same steps are used for benchmarking all of the above demos. For example:
+
+```
+$ cd helidon-quickstart-se
+$ make PREMAIN_HOME=/repos/leyden/build/linux-x64/images/jdk \
+       MAINLINE_HOME=/repos/jdk/build/linux-x64/images/jdk \
+       BLDJDK_HOME=/usr/local/jdk21 \
+       bench
+run,mainline default,mainline custom static CDS,premain custom static CDS only,premain CDS + AOT
+1,398,244,144,107
+2,387,247,142,108
+3,428,238,143,107
+4,391,252,142,111
+5,417,247,141,107
+6,390,239,139,127
+7,387,247,145,111
+8,387,240,147,110
+9,388,242,147,108
+10,400,242,167,108
+Geomean,397.08,243.76,145.52,110.26
+Stdev,13.55,4.19,7.50,5.73
+Markdown snippets in mainline_vs_premain.md
+```
+
+The above command runs each configuration 10 times, in an interleaving order. This way
+the noise of the system (background processes, thermo throttling, etc) is more likely to
+be spread across the different runs.
+
+As is typical for benchmarking start-up performance, the numbers are not very steady.
+It is best to plot
+the results (as saved in the file `mainline_vs_premain.csv`) in a spreadsheet to check for
+noise and other artifacts.
+
+The "make bench" target also generates GitHub markdown snippets (in the file `mainline_vs_premain.md`) for creating the
+graphs below.
+
+### Benchmarking Between Two Leyden Builds
+
+This is useful for Leyden developers to measure the benefits of a particular optimization.
+The steps are similar to above, but we use the "make compare_premain_builds" target:
+
+```
+$ cd helidon-quickstart-se
+$ make PM_OLD=/repos/leyden_old/build/linux-x64/images/jdk \
+       PM_NEW=/repos/leyden_new/build/linux-x64/images/jdk \
+       BLDJDK_HOME=/usr/local/jdk21 \
+       compare_premain_builds
+Old build = /repos/leyden_old/build/linux-x64/images/jdk with options
+New build = /repos/leyden_new/build/linux-x64/images/jdk with options
+Run,Old CDS + AOT,New CDS + AOT
+1,110,109
+2,131,111
+3,118,115
+4,110,108
+5,117,110
+6,114,109
+7,110,109
+8,118,110
+9,110,110
+10,113,114
+Geomean,114.94,110.48
+Stdev,6.19,2.16
+Markdown snippets in compare_premain_builds.md
+```
+
+Please see [test/hotspot/jtreg/premain/lib/Bench.gmk](test/hotspot/jtreg/premain/lib/Bench.gmk) for more details.
+
+Note: due to the variability of start-up time, the benefit of minor improvements may
+be difficult to measure.
+
+### Preliminary Benchmark Results
+
+The following charts show the relative start-up performance of the Leyden/Premain branch vs
+the JDK main-line.
+
+For example, a number of "premain CDS + AOT : 291" indicates that if the application takes
+1000 ms to start-up with the JDK main-line, it takes only 291 ms to start up when all the
+current set of Leyden optimizations for CDS and AOT are enabled.
+
+The benchmark results are collected with `make bench` in the following directories:
+
+- `helidon-quickstart-se`
+- `micronaut-first-app`
+- `quarkus-getting-started`
+- `spring-petclinic`
+
+These JDK versions were used in the comparisons:
+
+- JDK main-line: https://github.com/openjdk/jdk/commit/70944ca54ad0090c734bb5b3082beb33450c4877
+- Leyden: https://github.com/openjdk/leyden/commit/9fa972214934d30f67db5fd4d1b8007636ac1428
+
+The benchmarks were executed on an 8-core Intel i7-10700 CPU @ 2.90GHz with 32GB RAM running Ubuntu 22.04.3 LTS.
+
+### Helidon Quick Start (SE) Demo (3.44x improvement)
+
+```mermaid
+---
+config:
+    xyChart:
+        chartOrientation: horizontal
+        height: 300
+---
+xychart-beta
+    x-axis "variant" ["mainline default", "mainline custom static CDS", "premain custom static CDS only", "premain CDS + AOT"]
+    y-axis "Elapsed time (normalized, smaller is better)" 0 --> 1000
+    bar [1000, 632, 376, 291]
+```
+
+### Micronaut First App Demo (2.83x improvement)
+
+```mermaid
+---
+config:
+    xyChart:
+        chartOrientation: horizontal
+        height: 300
+---
+xychart-beta
+    x-axis "variant" ["mainline default", "mainline custom static CDS", "premain custom static CDS only", "premain CDS + AOT"]
+    y-axis "Elapsed time (normalized, smaller is better)" 0 --> 1000
+    bar [1000, 558, 410, 353]
+```
+
+### Quarkus Getting Started Demo (3.15x improvement)
+
+```mermaid
+---
+config:
+    xyChart:
+        chartOrientation: horizontal
+        height: 300
+---
+xychart-beta
+    x-axis "variant" ["mainline default", "mainline custom static CDS", "premain custom static CDS only", "premain CDS + AOT"]
+    y-axis "Elapsed time (normalized, smaller is better)" 0 --> 1000
+    bar [1000, 568, 395, 317]
+```
+
+### Spring-boot Getting Started Demo (3.53x improvement)
+
+```mermaid
+---
+config:
+    xyChart:
+        chartOrientation: horizontal
+        height: 300
+---
+xychart-beta
+    x-axis "variant" ["mainline default", "mainline custom static CDS", "premain custom static CDS only", "premain CDS + AOT"]
+    y-axis "Elapsed time (normalized, smaller is better)" 0 --> 1000
+    bar [1000, 560, 394, 283]
+```
+
+### Spring PetClinic Demo (2.72x improvement)
+
+```mermaid
+---
+config:
+    xyChart:
+        chartOrientation: horizontal
+        height: 300
+---
+xychart-beta
+    x-axis "variant" ["mainline default", "mainline custom static CDS", "premain custom static CDS only", "premain CDS + AOT"]
+    y-axis "Elapsed time (normalized, smaller is better)" 0 --> 1000
+    bar [1000, 695, 563, 368]
+```
+
+## 6. More Documentation
+
+Please see [test/hotspot/jtreg/premain/](test/hotspot/jtreg/premain) for more information.
diff a/make/InitSupport.gmk b/make/InitSupport.gmk
--- a/make/InitSupport.gmk
+++ b/make/InitSupport.gmk
@@ -459,11 +459,11 @@
 	      $(foreach logfile, $(sort $(wildcard $(MAKESUPPORT_OUTPUTDIR)/failure-logs/*.log)), \
 	          $(PRINTF) "* For target $(notdir $(basename $(logfile))):\n" ; \
 	          $(if $(filter all, $(LOG_REPORT)), \
 	            $(GREP) -v -e "^Note: including file:" <  $(logfile) || true ; \
 	          , \
-	            ($(GREP) -v -e "^Note: including file:" <  $(logfile) || true) | $(HEAD) -n 15 ; \
+	            ($(GREP) -v -e "^Note: including file:" <  $(logfile) || true); \
 	            if test `$(WC) -l < $(logfile)` -gt 15; then \
 	              $(ECHO) "   ... (rest of output omitted)" ; \
 	            fi ; \
 	          ) \
 	      ) \
diff a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad
--- a/src/hotspot/cpu/aarch64/aarch64.ad
+++ b/src/hotspot/cpu/aarch64/aarch64.ad
@@ -1139,11 +1139,11 @@
   //--------------------------------------------------------------
 
  public:
   // Size of call trampoline stub.
   static uint size_call_trampoline() {
-    return 0; // no call trampolines on this platform
+    return MacroAssembler::max_trampoline_stub_size(); // no call trampolines on this platform
   }
 
   // number of relocations needed by a call trampoline stub
   static uint reloc_call_trampoline() {
     return 0; // no call trampolines on this platform
@@ -3394,11 +3394,11 @@
       if (rtype == relocInfo::oop_type) {
         __ movoop(dst_reg, (jobject)con);
       } else if (rtype == relocInfo::metadata_type) {
         __ mov_metadata(dst_reg, (Metadata*)con);
       } else {
-        assert(rtype == relocInfo::none, "unexpected reloc type");
+        assert(rtype == relocInfo::none || rtype == relocInfo::external_word_type, "unexpected reloc type");
         if (! __ is_valid_AArch64_address(con) ||
             con < (address)(uintptr_t)os::vm_page_size()) {
           __ mov(dst_reg, con);
         } else {
           uint64_t offset;
@@ -3417,14 +3417,10 @@
   enc_class aarch64_enc_mov_p1(iRegP dst, immP_1 src) %{
     Register dst_reg = as_Register($dst$$reg);
     __ mov(dst_reg, (uint64_t)1);
   %}
 
-  enc_class aarch64_enc_mov_byte_map_base(iRegP dst, immByteMapBase src) %{
-    __ load_byte_map_base($dst$$Register);
-  %}
-
   enc_class aarch64_enc_mov_n(iRegN dst, immN src) %{
     Register dst_reg = as_Register($dst$$reg);
     address con = (address)$src$$constant;
     if (con == nullptr) {
       ShouldNotReachHere();
@@ -4522,11 +4518,23 @@
 // Card Table Byte Map Base
 operand immByteMapBase()
 %{
   // Get base of card map
   predicate(BarrierSet::barrier_set()->is_a(BarrierSet::CardTableBarrierSet) &&
-            (CardTable::CardValue*)n->get_ptr() == ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base());
+            is_card_table_address((address)(n->get_ptr())));
+  match(ConP);
+
+  op_cost(0);
+  format %{ %}
+  interface(CONST_INTER);
+%}
+
+// AOT Runtime Constants Address
+operand immAOTRuntimeConstantsAddress()
+%{
+  // Check if the address is in the range of AOT Runtime Constants
+  predicate(AOTRuntimeConstants::contains((address)(n->get_ptr())));
   match(ConP);
 
   op_cost(0);
   format %{ %}
   interface(CONST_INTER);
@@ -6816,11 +6824,27 @@
   match(Set dst con);
 
   ins_cost(INSN_COST);
   format %{ "adr  $dst, $con\t# Byte Map Base" %}
 
-  ins_encode(aarch64_enc_mov_byte_map_base(dst, con));
+  ins_encode %{
+    __ load_byte_map_base($dst$$Register);
+  %}
+
+  ins_pipe(ialu_imm);
+%}
+
+instruct loadAOTRCAddress(iRegPNoSp dst, immAOTRuntimeConstantsAddress con)
+%{
+  match(Set dst con);
+
+  ins_cost(INSN_COST);
+  format %{ "adr  $dst, $con\t# AOT Runtime Constants Address" %}
+
+  ins_encode %{
+    __ load_aotrc_address($dst$$Register, (address)$con$$constant);
+  %}
 
   ins_pipe(ialu_imm);
 %}
 
 // Load Narrow Pointer Constant
diff a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
@@ -227,11 +227,11 @@
   }
 
   static void spatch(address a, int msb, int lsb, int64_t val) {
     int nbits = msb - lsb + 1;
     int64_t chk = val >> (nbits - 1);
-    guarantee (chk == -1 || chk == 0, "Field too big for insn at " INTPTR_FORMAT, p2i(a));
+    guarantee (chk == -1 || chk == 0, "Field " INT64_FORMAT_X_0 " too big (nbits: %d) for insn at " INTPTR_FORMAT, val, nbits, p2i(a));
     uint64_t uval = val;
     unsigned mask = checked_cast<unsigned>(right_n_bits(nbits));
     uval &= mask;
     uval <<= lsb;
     mask <<= lsb;
@@ -260,11 +260,11 @@
   }
 
   void sf(int64_t val, int msb, int lsb) {
     int nbits = msb - lsb + 1;
     int64_t chk = val >> (nbits - 1);
-    guarantee (chk == -1 || chk == 0, "Field too big for insn");
+    guarantee (chk == -1 || chk == 0, "Field " INT64_FORMAT_X_0 " too big (nbits: %d) for insn", val, nbits);
     uint64_t uval = val;
     unsigned mask = checked_cast<unsigned>(right_n_bits(nbits));
     uval &= mask;
     f((unsigned)uval, lsb + nbits - 1, lsb);
   }
diff a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
@@ -32,10 +32,12 @@
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArrayKlass.hpp"
 #include "ci/ciInstance.hpp"
+#include "ci/ciUtilities.hpp"
+#include "code/SCCache.hpp"
 #include "code/compiledIC.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gc_globals.hpp"
 #include "nativeInst_aarch64.hpp"
 #include "oops/objArrayKlass.hpp"
@@ -527,10 +529,22 @@
       break;
     }
 
     case T_LONG: {
       assert(patch_code == lir_patch_none, "no patching handled here");
+      if (SCCache::is_on_for_write()) {
+        // SCA needs relocation info for card table base
+        address b = c->as_pointer();
+        if (is_card_table_address(b)) {
+          __ lea(dest->as_register_lo(), ExternalAddress(b));
+          break;
+        }
+        if (AOTRuntimeConstants::contains(b)) {
+          __ load_aotrc_address(dest->as_register_lo(), b);
+          break;
+        }
+      }
       __ mov(dest->as_register_lo(), (intptr_t)c->as_jlong());
       break;
     }
 
     case T_OBJECT: {
diff a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
@@ -22,10 +22,13 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "asm/macroAssembler.inline.hpp"
+#if INCLUDE_CDS
+#include "code/SCCache.hpp"
+#endif
 #include "gc/g1/g1BarrierSet.hpp"
 #include "gc/g1/g1BarrierSetAssembler.hpp"
 #include "gc/g1/g1BarrierSetRuntime.hpp"
 #include "gc/g1/g1CardTable.hpp"
 #include "gc/g1/g1HeapRegion.hpp"
@@ -206,19 +209,50 @@
                                             const Register tmp1,
                                             const Register tmp2,
                                             Label& done,
                                             bool new_val_may_be_null) {
   // Does store cross heap regions?
-  __ eor(tmp1, store_addr, new_val);                     // tmp1 := store address ^ new value
-  __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes);   // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
-  __ cbz(tmp1, done);
+#if INCLUDE_CDS
+  // AOT code needs to load the barrier grain shift from the aot
+  // runtime constants area in the code cache otherwise we can compile
+  // it as an immediate operand
+  if (SCCache::is_on_for_write()) {
+    address grain_shift_address = (address)AOTRuntimeConstants::grain_shift_address();
+    __ eor(tmp1, store_addr, new_val);
+    __ lea(tmp2, ExternalAddress(grain_shift_address));
+    __ ldrb(tmp2, tmp2);
+    __ lsrv(tmp1, tmp1, tmp2);
+    __ cbz(tmp1, done);
+  } else
+#endif
+  {
+    __ eor(tmp1, store_addr, new_val);                     // tmp1 := store address ^ new value
+    __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes);   // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
+    __ cbz(tmp1, done);
+  }
+
   // Crosses regions, storing null?
   if (new_val_may_be_null) {
     __ cbz(new_val, done);
   }
   // Storing region crossing non-null, is card young?
-  __ lsr(tmp1, store_addr, CardTable::card_shift());     // tmp1 := card address relative to card table base
+
+#if INCLUDE_CDS
+  // AOT code needs to load the barrier card shift from the aot
+  // runtime constants area in the code cache otherwise we can compile
+  // it as an immediate operand
+  if (SCCache::is_on_for_write()) {
+    address card_shift_address = (address)AOTRuntimeConstants::card_shift_address();
+    __ lea(tmp2, ExternalAddress(card_shift_address));
+    __ ldrb(tmp2, tmp2);
+    __ lsrv(tmp1, store_addr, tmp2);                        // tmp1 := card address relative to card table base
+  } else
+#endif
+  {
+    __ lsr(tmp1, store_addr, CardTable::card_shift());     // tmp1 := card address relative to card table base
+  }
+
   __ load_byte_map_base(tmp2);                           // tmp2 := card table base address
   __ add(tmp1, tmp1, tmp2);                              // tmp1 := card address
   __ ldrb(tmp2, Address(tmp1));                          // tmp2 := card
   __ cmpw(tmp2, (int)G1CardTable::g1_young_card_val());  // tmp2 := card == young_card_val?
 }
@@ -280,11 +314,11 @@
   SaveLiveRegisters save_registers(masm, stub);
   if (c_rarg0 != arg) {
     __ mov(c_rarg0, arg);
   }
   __ mov(c_rarg1, rthread);
-  __ mov(rscratch1, runtime_path);
+  __ lea(rscratch1, RuntimeAddress(runtime_path));
   __ blr(rscratch1);
 }
 
 void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm,
                                                     Register obj,
diff a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
@@ -42,10 +42,11 @@
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "runtime/basicLock.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/javaThread.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/safepointMechanism.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "utilities/powerOfTwo.hpp"
 
 void InterpreterMacroAssembler::narrow(Register result) {
@@ -1396,10 +1397,20 @@
   }
 }
 
 void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) { ; }
 
+void InterpreterMacroAssembler::generate_runtime_upcalls_on_method_entry()
+{
+  address upcall = RuntimeUpcalls::on_method_entry_upcall_address();
+  if (RuntimeUpcalls::does_upcall_need_method_parameter(upcall)) {
+    get_method(c_rarg1);
+    call_VM(noreg,upcall, c_rarg1);
+  } else {
+    call_VM(noreg,upcall);
+  }
+}
 
 void InterpreterMacroAssembler::notify_method_entry() {
   // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to
   // track stack depth.  If it is possible to enter interp_only_mode we add
   // the code to check if the event should be sent.
@@ -1417,11 +1428,12 @@
     call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry),
                  rthread, c_rarg1);
   }
 
   // RedefineClasses() tracing support for obsolete method entry
-  if (log_is_enabled(Trace, redefine, class, obsolete)) {
+  if (log_is_enabled(Trace, redefine, class, obsolete) ||
+      log_is_enabled(Trace, interpreter, bytecode)) {
     get_method(c_rarg1);
     call_VM_leaf(
       CAST_FROM_FN_PTR(address, SharedRuntime::rc_trace_method_entry),
       rthread, c_rarg1);
   }
diff a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
@@ -306,10 +306,13 @@
   // only if +VerifyFPU  && (state == ftos || state == dtos)
   void verify_FPU(int stack_depth, TosState state = ftos);
 
   typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode;
 
+  // support for runtime upcalls
+  void generate_runtime_upcalls_on_method_entry();
+
   // support for jvmti/dtrace
   void notify_method_entry();
   void notify_method_exit(TosState state, NotifyMethodExitMode mode);
 
   virtual void _call_Unimplemented(address call_site) {
diff a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -25,11 +25,15 @@
 
 #include "precompiled.hpp"
 #include "asm/assembler.hpp"
 #include "asm/assembler.inline.hpp"
 #include "ci/ciEnv.hpp"
+#include "ci/ciUtilities.hpp"
 #include "code/compiledIC.hpp"
+#if INCLUDE_CDS
+#include "code/SCCache.hpp"
+#endif
 #include "compiler/compileTask.hpp"
 #include "compiler/disassembler.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
@@ -347,10 +351,20 @@
     dest = (dest & 0xffffffffULL) | (uintptr_t(insn_addr) & 0xffff00000000ULL);
     target = address(dest);
     return 2;
   }
   virtual int immediate(address insn_addr, address &target) {
+    // Metadata pointers are either narrow (32 bits) or wide (48 bits).
+    // We encode narrow ones by setting the upper 16 bits in the first
+    // instruction.
+    if (Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010101) {
+      assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
+      narrowKlass nk = CompressedKlassPointers::encode((Klass*)target);
+      Instruction_aarch64::patch(insn_addr, 20, 5, nk >> 16);
+      Instruction_aarch64::patch(insn_addr+4, 20, 5, nk & 0xffff);
+      return 2;
+    }
     assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be");
     uint64_t dest = (uint64_t)target;
     // Move wide constant
     assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
     assert(nativeInstruction_at(insn_addr+8)->is_movk(), "wrong insns in patch");
@@ -477,10 +491,20 @@
       return 2;
     }
   }
   virtual int immediate(address insn_addr, address &target) {
     uint32_t *insns = (uint32_t *)insn_addr;
+    // Metadata pointers are either narrow (32 bits) or wide (48 bits).
+    // We encode narrow ones by setting the upper 16 bits in the first
+    // instruction.
+    if (Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010101) {
+      assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
+      narrowKlass nk = (narrowKlass)((uint32_t(Instruction_aarch64::extract(_insn, 20, 5)) << 16)
+                                   +  uint32_t(Instruction_aarch64::extract(insns[1], 20, 5)));
+      target = (address)CompressedKlassPointers::decode(nk);
+      return 2;
+    }
     assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be");
     // Move wide constant: movz, movk, movk.  See movptr().
     assert(nativeInstruction_at(insns+1)->is_movk(), "wrong insns in patch");
     assert(nativeInstruction_at(insns+2)->is_movk(), "wrong insns in patch");
     target = address(uint64_t(Instruction_aarch64::extract(_insn, 20, 5))
@@ -674,10 +698,13 @@
     set_last_Java_frame(last_java_sp, last_java_fp, pc() /* Patched later */, scratch);
   }
 }
 
 static inline bool target_needs_far_branch(address addr) {
+  if (SCCache::is_on_for_write()) {
+    return true;
+  }
   // codecache size <= 128M
   if (!MacroAssembler::far_branches()) {
     return false;
   }
   // codecache size > 240M
@@ -858,10 +885,13 @@
   call_VM_base(oop_result, noreg, noreg, entry_point, number_of_arguments, check_exceptions);
 }
 
 // Check the entry target is always reachable from any branch.
 static bool is_always_within_branch_range(Address entry) {
+  if (SCCache::is_on_for_write()) {
+    return false;
+  }
   const address target = entry.target();
 
   if (!CodeCache::contains(target)) {
     // We always use trampolines for callees outside CodeCache.
     assert(entry.rspec().type() == relocInfo::runtime_call_type, "non-runtime call of an external target");
@@ -3234,12 +3264,15 @@
   bind(done);
 }
 
 void MacroAssembler::stop(const char* msg) {
   BLOCK_COMMENT(msg);
+  // load msg into r0 so we can access it from the signal handler
+  // ExternalAddress enables saving and restoring via the code cache
+  lea(c_rarg0, ExternalAddress((address) msg));
   dcps1(0xdeae);
-  emit_int64((uintptr_t)msg);
+  SCCache::add_C_string(msg);
 }
 
 void MacroAssembler::unimplemented(const char* what) {
   const char* buf = nullptr;
   {
@@ -3333,11 +3366,11 @@
 }
 
 void MacroAssembler::reinit_heapbase()
 {
   if (UseCompressedOops) {
-    if (Universe::is_fully_initialized()) {
+    if (Universe::is_fully_initialized() && !SCCache::is_on_for_write()) {
       mov(rheapbase, CompressedOops::base());
     } else {
       lea(rheapbase, ExternalAddress(CompressedOops::base_addr()));
       ldr(rheapbase, Address(rheapbase));
     }
@@ -5687,11 +5720,34 @@
   CardTable::CardValue* byte_map_base =
     ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base();
 
   // Strictly speaking the byte_map_base isn't an address at all, and it might
   // even be negative. It is thus materialised as a constant.
-  mov(reg, (uint64_t)byte_map_base);
+#if INCLUDE_CDS
+  if (SCCache::is_on_for_write()) {
+    // SCA needs relocation info for card table base
+    lea(reg, ExternalAddress(reinterpret_cast<address>(byte_map_base)));
+  } else {
+#endif
+    mov(reg, (uint64_t)byte_map_base);
+#if INCLUDE_CDS
+  }
+#endif
+}
+
+void MacroAssembler::load_aotrc_address(Register reg, address a) {
+#if INCLUDE_CDS
+  assert(AOTRuntimeConstants::contains(a), "address out of range for data area");
+  if (SCCache::is_on_for_write()) {
+    // all aotrc field addresses should be registered in the SCC address table
+    lea(reg, ExternalAddress(a));
+  } else {
+    mov(reg, (uint64_t)a);
+  }
+#else
+  ShouldNotReachHere();
+#endif
 }
 
 void MacroAssembler::build_frame(int framesize) {
   assert(framesize >= 2 * wordSize, "framesize must include space for FP/LR");
   assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment");
diff a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
@@ -26,10 +26,11 @@
 #ifndef CPU_AARCH64_MACROASSEMBLER_AARCH64_HPP
 #define CPU_AARCH64_MACROASSEMBLER_AARCH64_HPP
 
 #include "asm/assembler.inline.hpp"
 #include "code/vmreg.hpp"
+#include "code/SCCache.hpp"
 #include "metaprogramming/enableIf.hpp"
 #include "oops/compressedOops.hpp"
 #include "oops/compressedKlass.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/powerOfTwo.hpp"
@@ -1299,10 +1300,14 @@
     return ReservedCodeCacheSize > branch_range;
   }
 
   // Check if branches to the non nmethod section require a far jump
   static bool codestub_branch_needs_far_jump() {
+    if (SCCache::is_on_for_write()) {
+      // To calculate far_codestub_branch_size correctly.
+      return true;
+    }
     return CodeCache::max_distance_to_non_nmethod() > branch_range;
   }
 
   // Emit a direct call/jump if the entry address will always be in range,
   // otherwise a far call/jump.
@@ -1446,10 +1451,13 @@
   }
 
   // Load the base of the cardtable byte map into reg.
   void load_byte_map_base(Register reg);
 
+  // Load a constant address in the AOT Runtime Constants area
+  void load_aotrc_address(Register reg, address a);
+
   // Prolog generator routines to support switch between x86 code and
   // generated ARM code
 
   // routine to generate an x86 prolog for a stub function which
   // bootstraps into the generated ARM code which directly follows the
diff a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
@@ -117,5 +117,13 @@
   }
 }
 
 void metadata_Relocation::pd_fix_value(address x) {
 }
+
+address trampoline_stub_Relocation::pd_destination() {
+  return nativeCallTrampolineStub_at(addr())->destination();
+}
+
+void trampoline_stub_Relocation::pd_set_destination(address x) {
+  nativeCallTrampolineStub_at(addr())->set_destination(x);
+}
diff a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
@@ -26,10 +26,11 @@
 #include "precompiled.hpp"
 #include "asm/macroAssembler.hpp"
 #include "asm/macroAssembler.inline.hpp"
 #include "asm/register.hpp"
 #include "atomic_aarch64.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/gc_globals.hpp"
 #include "gc/shared/tlab_globals.hpp"
@@ -4674,10 +4675,14 @@
   address generate_multiplyToLen() {
     __ align(CodeEntryAlignment);
     StubCodeMark mark(this, "StubRoutines", "multiplyToLen");
 
     address start = __ pc();
+ 
+    if (SCCache::load_stub(this, vmIntrinsics::_multiplyToLen, "multiplyToLen", start)) {
+      return start;
+    }
     const Register x     = r0;
     const Register xlen  = r1;
     const Register y     = r2;
     const Register ylen  = r3;
     const Register z     = r4;
@@ -4695,10 +4700,11 @@
     __ enter(); // required for proper stackwalking of RuntimeStub frame
     __ multiply_to_len(x, xlen, y, ylen, z, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7);
     __ leave(); // required for proper stackwalking of RuntimeStub frame
     __ ret(lr);
 
+    SCCache::store_stub(this, vmIntrinsics::_multiplyToLen, "multiplyToLen", start);
     return start;
   }
 
   address generate_squareToLen() {
     // squareToLen algorithm for sizes 1..127 described in java code works
@@ -4706,10 +4712,13 @@
     // multiply_to_len shows a bit better overall results
     __ align(CodeEntryAlignment);
     StubCodeMark mark(this, "StubRoutines", "squareToLen");
     address start = __ pc();
 
+    if (SCCache::load_stub(this, vmIntrinsics::_squareToLen, "squareToLen", start)) {
+      return start;
+    }
     const Register x     = r0;
     const Register xlen  = r1;
     const Register z     = r2;
     const Register y     = r4; // == x
     const Register ylen  = r5; // == xlen
@@ -4731,19 +4740,24 @@
     __ mov(ylen, xlen);
     __ multiply_to_len(x, xlen, y, ylen, z, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7);
     __ pop(spilled_regs, sp);
     __ leave();
     __ ret(lr);
+
+    SCCache::store_stub(this, vmIntrinsics::_squareToLen, "squareToLen", start);
     return start;
   }
 
   address generate_mulAdd() {
     __ align(CodeEntryAlignment);
     StubCodeMark mark(this, "StubRoutines", "mulAdd");
 
     address start = __ pc();
 
+    if (SCCache::load_stub(this, vmIntrinsics::_mulAdd, "mulAdd", start)) {
+      return start;
+    }
     const Register out     = r0;
     const Register in      = r1;
     const Register offset  = r2;
     const Register len     = r3;
     const Register k       = r4;
@@ -4752,10 +4766,11 @@
     __ enter();
     __ mul_add(out, in, offset, len, k);
     __ leave();
     __ ret(lr);
 
+    SCCache::store_stub(this, vmIntrinsics::_mulAdd, "mulAdd", start);
     return start;
   }
 
   // Arguments:
   //
diff a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
@@ -1183,11 +1183,11 @@
 }
 
 // Interpreter stub for calling a native method. (asm interpreter)
 // This sets up a somewhat different looking stack for calling the
 // native method than the typical interpreter frame setup.
-address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_native_entry(bool synchronized, bool runtime_upcalls) {
   // determine code generation flags
   bool inc_counter  = UseCompiler || CountCompiledCalls;
 
   // r1: Method*
   // rscratch1: sender sp
@@ -1302,10 +1302,14 @@
 #endif
 
   // jvmti support
   __ notify_method_entry();
 
+  if (runtime_upcalls) {
+    __ generate_runtime_upcalls_on_method_entry();
+  }
+
   // work registers
   const Register t = r17;
   const Register result_handler = r19;
 
   // allocate space for parameters
@@ -1631,11 +1635,11 @@
 }
 
 //
 // Generic interpreted method entry to (asm) interpreter
 //
-address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized, bool runtime_upcalls) {
   // determine code generation flags
   bool inc_counter  = UseCompiler || CountCompiledCalls;
 
   // rscratch1: sender sp
   address entry_point = __ pc();
@@ -1776,10 +1780,15 @@
 #endif
 
   // jvmti support
   __ notify_method_entry();
 
+  // runtime upcalls
+  if (runtime_upcalls) {
+    __ generate_runtime_upcalls_on_method_entry();
+  }
+
   __ dispatch_next(vtos);
 
   // invocation counter overflow
   if (inc_counter) {
     // Handle overflow of counter and compile method
@@ -2077,10 +2086,28 @@
   generate_and_dispatch(t);
 }
 
 //-----------------------------------------------------------------------------
 
+void TemplateInterpreterGenerator::count_bytecode() {
+  if (CountBytecodesPerThread) {
+    Address bc_counter_addr(rthread, Thread::bc_counter_offset());
+    __ ldr(r10, bc_counter_addr);
+    __ add(r10, r10, 1);
+    __ str(r10, bc_counter_addr);
+  }
+  if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) {
+    __ mov(r10, (address) &BytecodeCounter::_counter_value);
+    __ atomic_add(noreg, 1, r10);
+  }
+}
+
+void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
+  __ mov(r10, (address) &BytecodeHistogram::_counters[t->bytecode()]);
+  __ atomic_addw(noreg, 1, r10);
+}
+
 // Non-product code
 #ifndef PRODUCT
 address TemplateInterpreterGenerator::generate_trace_code(TosState state) {
   address entry = __ pc();
 
@@ -2099,20 +2126,10 @@
   __ ret(lr);                                   // return from result handler
 
   return entry;
 }
 
-void TemplateInterpreterGenerator::count_bytecode() {
-  __ mov(r10, (address) &BytecodeCounter::_counter_value);
-  __ atomic_addw(noreg, 1, r10);
-}
-
-void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
-  __ mov(r10, (address) &BytecodeHistogram::_counters[t->bytecode()]);
-  __ atomic_addw(noreg, 1, r10);
-}
-
 void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) {
   // Calculate new index for counter:
   //   _index = (_index >> log2_number_of_codes) |
   //            (bytecode << log2_number_of_codes);
   Register index_addr = rscratch1;
diff a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
@@ -351,10 +351,22 @@
 
   // resolved class - need to call vm to get java mirror of the class
   __ cmp(r3, (u1)JVM_CONSTANT_Class);
   __ br(Assembler::NE, notClass);
 
+  __ load_resolved_klass_at_offset(r2, r1, r3, rscratch1); // kills r3=tag
+
+  __ cmp(r3, zr); // resolved_klass ?= null
+  __ br(Assembler::EQ, call_ldc);
+
+  const int mirror_offset = in_bytes(Klass::java_mirror_offset());
+  __ ldr(r3, Address(r3, mirror_offset));
+  __ resolve_oop_handle(r3, rscratch1, rscratch2);
+  __ push_ptr(r3);
+
+  __ b(Done);
+
   __ bind(call_ldc);
   __ mov(c_rarg1, is_ldc_wide(type) ? 1 : 0);
   call_VM(r0, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), c_rarg1);
   __ push_ptr(r0);
   __ verify_oop(r0);
@@ -2308,11 +2320,11 @@
                                             Register Rcache,
                                             Register index) {
   const Register temp = r19;
   assert_different_registers(Rcache, index, temp);
 
-  Label resolved;
+  Label resolved, clinit_barrier_slow;
 
   Bytecodes::Code code = bytecode();
   switch (code) {
   case Bytecodes::_nofast_getfield: code = Bytecodes::_getfield; break;
   case Bytecodes::_nofast_putfield: code = Bytecodes::_putfield; break;
@@ -2330,17 +2342,25 @@
   __ ldarb(temp, temp);
   __ subs(zr, temp, (int) code);  // have we resolved this bytecode?
   __ br(Assembler::EQ, resolved);
 
   // resolve first time through
+  __ bind(clinit_barrier_slow);
   address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache);
   __ mov(temp, (int) code);
   __ call_VM(noreg, entry, temp);
 
   // Update registers with resolved info
   __ load_field_entry(Rcache, index);
   __ bind(resolved);
+
+  // Class initialization barrier for static fields
+  if (VM_Version::supports_fast_class_init_checks() &&
+      (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) {
+    __ ldr(temp, Address(Rcache, ResolvedFieldEntry::field_holder_offset()));
+    __ clinit_barrier(temp, rscratch1, nullptr, &clinit_barrier_slow);
+  }
 }
 
 void TemplateTable::load_resolved_field_entry(Register obj,
                                               Register cache,
                                               Register tos_state,
diff a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp
--- a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp
+++ b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp
@@ -806,11 +806,11 @@
 // Interpreter stub for calling a native method. (asm interpreter)
 // This sets up a somewhat different looking stack for calling the native method
 // than the typical interpreter frame setup.
 //
 
-address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_native_entry(bool synchronized, bool runtime_upcalls) {
   // determine code generation flags
   bool inc_counter  = UseCompiler || CountCompiledCalls;
 
   // Incoming registers:
   //
@@ -1140,11 +1140,11 @@
 }
 
 //
 // Generic interpreted method entry to (asm) interpreter
 //
-address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized, bool runtime_upcalls) {
   // determine code generation flags
   bool inc_counter  = UseCompiler || CountCompiledCalls;
 
   // Rmethod: Method*
   // Rthread: thread
@@ -1564,10 +1564,19 @@
   generate_and_dispatch(t);
 }
 
 //------------------------------------------------------------------------------------------------------------------------
 
+void TemplateInterpreterGenerator::count_bytecode() {
+  __ inc_global_counter((address) &BytecodeCounter::_counter_value, 0, Rtemp, R2_tmp, true);
+}
+
+
+void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
+  __ inc_global_counter((address)&BytecodeHistogram::_counters[0], sizeof(BytecodeHistogram::_counters[0]) * t->bytecode(), Rtemp, R2_tmp, true);
+}
+
 // Non-product code
 #ifndef PRODUCT
 address TemplateInterpreterGenerator::generate_trace_code(TosState state) {
   address entry = __ pc();
 
@@ -1589,21 +1598,10 @@
   __ ret();
 
   return entry;
 }
 
-
-void TemplateInterpreterGenerator::count_bytecode() {
-  __ inc_global_counter((address) &BytecodeCounter::_counter_value, 0, Rtemp, R2_tmp, true);
-}
-
-
-void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
-  __ inc_global_counter((address)&BytecodeHistogram::_counters[0], sizeof(BytecodeHistogram::_counters[0]) * t->bytecode(), Rtemp, R2_tmp, true);
-}
-
-
 void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) {
   const Register Rindex_addr = R2_tmp;
   Label Lcontinue;
   InlinedAddress Lcounters((address)BytecodePairHistogram::_counters);
   InlinedAddress Lindex((address)&BytecodePairHistogram::_index);
diff a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp
--- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp
+++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp
@@ -1186,11 +1186,11 @@
 //   R15_esp       - intptr_t* sender tos
 //
 //   abstract stack (grows up)
 //     [  IJava (caller of JNI callee)  ]  <-- ASP
 //        ...
-address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_native_entry(bool synchronized, bool runtime_upcalls) {
 
   address entry = __ pc();
 
   const bool inc_counter = UseCompiler || CountCompiledCalls;
 
@@ -1645,11 +1645,11 @@
   return entry;
 }
 
 // Generic interpreted method entry to (asm) interpreter.
 //
-address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized, bool runtime_upcalls) {
   bool inc_counter = UseCompiler || CountCompiledCalls;
   address entry = __ pc();
   // Generate the code to allocate the interpreter stack frame.
   Register Rsize_of_parameters = R4_ARG2, // Written by generate_fixed_frame.
            Rsize_of_locals     = R5_ARG3; // Written by generate_fixed_frame.
@@ -2218,10 +2218,24 @@
   generate_and_dispatch(t);
 }
 
 //-----------------------------------------------------------------------------
 
+void TemplateInterpreterGenerator::count_bytecode() {
+  int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeCounter::_counter_value, R12_scratch2, true);
+  __ lwz(R12_scratch2, offs, R11_scratch1);
+  __ addi(R12_scratch2, R12_scratch2, 1);
+  __ stw(R12_scratch2, offs, R11_scratch1);
+}
+
+void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
+  int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeHistogram::_counters[t->bytecode()], R12_scratch2, true);
+  __ lwz(R12_scratch2, offs, R11_scratch1);
+  __ addi(R12_scratch2, R12_scratch2, 1);
+  __ stw(R12_scratch2, offs, R11_scratch1);
+}
+
 // Non-product code
 #ifndef PRODUCT
 address TemplateInterpreterGenerator::generate_trace_code(TosState state) {
   //__ flush_bundle();
   address entry = __ pc();
@@ -2305,24 +2319,10 @@
   __ blr();
   BLOCK_COMMENT("} trace_code");
   return entry;
 }
 
-void TemplateInterpreterGenerator::count_bytecode() {
-  int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeCounter::_counter_value, R12_scratch2, true);
-  __ lwz(R12_scratch2, offs, R11_scratch1);
-  __ addi(R12_scratch2, R12_scratch2, 1);
-  __ stw(R12_scratch2, offs, R11_scratch1);
-}
-
-void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
-  int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeHistogram::_counters[t->bytecode()], R12_scratch2, true);
-  __ lwz(R12_scratch2, offs, R11_scratch1);
-  __ addi(R12_scratch2, R12_scratch2, 1);
-  __ stw(R12_scratch2, offs, R11_scratch1);
-}
-
 void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) {
   const Register addr = R11_scratch1,
                  tmp  = R12_scratch2;
   // Get index, shift out old bytecode, bring in new bytecode, and store it.
   // _index = (_index >> log2_number_of_codes) |
diff a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
--- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
@@ -986,11 +986,11 @@
 }
 
 // Interpreter stub for calling a native method. (asm interpreter)
 // This sets up a somewhat different looking stack for calling the
 // native method than the typical interpreter frame setup.
-address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_native_entry(bool synchronized, bool runtime_upcalls) {
   // determine code generation flags
   bool inc_counter = UseCompiler || CountCompiledCalls;
 
   // x11: Method*
   // x30: sender sp
@@ -1409,11 +1409,11 @@
 }
 
 //
 // Generic interpreted method entry to (asm) interpreter
 //
-address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized, bool runtime_upcalls) {
 
   // determine code generation flags
   const bool inc_counter  = UseCompiler || CountCompiledCalls;
 
   // t0: sender sp
@@ -1825,10 +1825,20 @@
   generate_and_dispatch(t);
 }
 
 //-----------------------------------------------------------------------------
 
+void TemplateInterpreterGenerator::count_bytecode() {
+  __ mv(x7, (address) &BytecodeCounter::_counter_value);
+  __ atomic_addw(noreg, 1, x7);
+}
+
+void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
+  __ mv(x7, (address) &BytecodeHistogram::_counters[t->bytecode()]);
+  __ atomic_addw(noreg, 1, x7);
+}
+
 // Non-product code
 #ifndef PRODUCT
 address TemplateInterpreterGenerator::generate_trace_code(TosState state) {
   address entry = __ pc();
 
@@ -1843,20 +1853,10 @@
   __ ret();                                   // return from result handler
 
   return entry;
 }
 
-void TemplateInterpreterGenerator::count_bytecode() {
-  __ mv(x7, (address) &BytecodeCounter::_counter_value);
-  __ atomic_addw(noreg, 1, x7);
-}
-
-void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
-  __ mv(x7, (address) &BytecodeHistogram::_counters[t->bytecode()]);
-  __ atomic_addw(noreg, 1, x7);
-}
-
 void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) {
   // Calculate new index for counter:
   //   _index = (_index >> log2_number_of_codes) |
   //            (bytecode << log2_number_of_codes);
   Register index_addr = t1;
diff a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp
--- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp
+++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp
@@ -3178,11 +3178,10 @@
     }
     if (UseSHA512Intrinsics) {
       StubRoutines::_sha512_implCompress   = generate_SHA512_stub(false, "SHA512_singleBlock");
       StubRoutines::_sha512_implCompressMB = generate_SHA512_stub(true,  "SHA512_multiBlock");
     }
-
 #ifdef COMPILER2
     if (UseMultiplyToLenIntrinsic) {
       StubRoutines::_multiplyToLen = generate_multiplyToLen();
     }
     if (UseMontgomeryMultiplyIntrinsic) {
diff a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp
--- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp
+++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp
@@ -1304,11 +1304,11 @@
 }
 
 // Interpreter stub for calling a native method. (asm interpreter).
 // This sets up a somewhat different looking stack for calling the
 // native method than the typical interpreter frame setup.
-address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_native_entry(bool synchronized, bool runtime_upcalls) {
   // Determine code generation flags.
   bool inc_counter = UseCompiler || CountCompiledCalls;
 
   // Interpreter entry for ordinary Java methods.
   //
@@ -1660,11 +1660,11 @@
 }
 
 //
 // Generic interpreted method entry to template interpreter.
 //
-address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized, bool runtime_upcalls) {
   address entry_point = __ pc();
 
   bool inc_counter = UseCompiler || CountCompiledCalls;
 
   // Interpreter entry for ordinary Java methods.
@@ -2305,10 +2305,21 @@
   generate_and_dispatch(t);
 }
 
 //-----------------------------------------------------------------------------
 
+// Make feasible for old CPUs.
+void TemplateInterpreterGenerator::count_bytecode() {
+  __ load_absolute_address(Z_R1_scratch, (address) &BytecodeCounter::_counter_value);
+  __ add2mem_32(Address(Z_R1_scratch), 1, Z_R0_scratch);
+}
+
+void TemplateInterpreterGenerator::histogram_bytecode(Template * t) {
+  __ load_absolute_address(Z_R1_scratch, (address)&BytecodeHistogram::_counters[ t->bytecode() ]);
+  __ add2mem_32(Address(Z_R1_scratch), 1, Z_tmp_1);
+}
+
 #ifndef PRODUCT
 address TemplateInterpreterGenerator::generate_trace_code(TosState state) {
   address entry = __ pc();
   NearLabel counter_below_trace_threshold;
 
@@ -2341,21 +2352,10 @@
   __ z_br(Z_R14); // return
 
   return entry;
 }
 
-// Make feasible for old CPUs.
-void TemplateInterpreterGenerator::count_bytecode() {
-  __ load_absolute_address(Z_R1_scratch, (address) &BytecodeCounter::_counter_value);
-  __ add2mem_32(Address(Z_R1_scratch), 1, Z_R0_scratch);
-}
-
-void TemplateInterpreterGenerator::histogram_bytecode(Template * t) {
-  __ load_absolute_address(Z_R1_scratch, (address)&BytecodeHistogram::_counters[ t->bytecode() ]);
-  __ add2mem_32(Address(Z_R1_scratch), 1, Z_tmp_1);
-}
-
 void TemplateInterpreterGenerator::histogram_bytecode_pair(Template * t) {
   Address  index_addr(Z_tmp_1, (intptr_t) 0);
   Register index = Z_tmp_2;
 
   // Load previous index.
diff a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
@@ -31,10 +31,12 @@
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArrayKlass.hpp"
 #include "ci/ciInstance.hpp"
+#include "ci/ciUtilities.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gc_globals.hpp"
 #include "nativeInst_x86.hpp"
 #include "oops/objArrayKlass.hpp"
@@ -49,28 +51,28 @@
 // These masks are used to provide 128-bit aligned bitmasks to the XMM
 // instructions, to allow sign-masking or sign-bit flipping.  They allow
 // fast versions of NegF/NegD and AbsF/AbsD.
 
 // Note: 'double' and 'long long' have 32-bits alignment on x86.
-static jlong* double_quadword(jlong *adr, jlong lo, jlong hi) {
+static address double_quadword(jlong *adr, jlong lo, jlong hi) {
   // Use the expression (adr)&(~0xF) to provide 128-bits aligned address
   // of 128-bits operands for SSE instructions.
   jlong *operand = (jlong*)(((intptr_t)adr) & ((intptr_t)(~0xF)));
   // Store the value to a 128-bits operand.
   operand[0] = lo;
   operand[1] = hi;
-  return operand;
+  return (address)operand;
 }
 
 // Buffer for 128-bits masks used by SSE instructions.
 static jlong fp_signmask_pool[(4+1)*2]; // 4*128bits(data) + 128bits(alignment)
 
 // Static initialization during VM startup.
-static jlong *float_signmask_pool  = double_quadword(&fp_signmask_pool[1*2],         CONST64(0x7FFFFFFF7FFFFFFF),         CONST64(0x7FFFFFFF7FFFFFFF));
-static jlong *double_signmask_pool = double_quadword(&fp_signmask_pool[2*2],         CONST64(0x7FFFFFFFFFFFFFFF),         CONST64(0x7FFFFFFFFFFFFFFF));
-static jlong *float_signflip_pool  = double_quadword(&fp_signmask_pool[3*2], (jlong)UCONST64(0x8000000080000000), (jlong)UCONST64(0x8000000080000000));
-static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], (jlong)UCONST64(0x8000000000000000), (jlong)UCONST64(0x8000000000000000));
+address LIR_Assembler::float_signmask_pool  = double_quadword(&fp_signmask_pool[1*2],         CONST64(0x7FFFFFFF7FFFFFFF),         CONST64(0x7FFFFFFF7FFFFFFF));
+address LIR_Assembler::double_signmask_pool = double_quadword(&fp_signmask_pool[2*2],         CONST64(0x7FFFFFFFFFFFFFFF),         CONST64(0x7FFFFFFFFFFFFFFF));
+address LIR_Assembler::float_signflip_pool  = double_quadword(&fp_signmask_pool[3*2], (jlong)UCONST64(0x8000000080000000), (jlong)UCONST64(0x8000000080000000));
+address LIR_Assembler::double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], (jlong)UCONST64(0x8000000000000000), (jlong)UCONST64(0x8000000000000000));
 
 
 NEEDS_CLEANUP // remove this definitions ?
 const Register SYNC_header = rax;   // synchronization header
 const Register SHIFT_count = rcx;   // where count for shift operations must be
@@ -573,10 +575,22 @@
     }
 
     case T_LONG: {
       assert(patch_code == lir_patch_none, "no patching handled here");
 #ifdef _LP64
+      if (SCCache::is_on_for_write()) {
+        // SCA needs relocation info for card table base
+        address b = c->as_pointer();
+        if (is_card_table_address(b)) {
+          __ lea(dest->as_register_lo(), ExternalAddress(b));
+          break;
+        }
+        if (AOTRuntimeConstants::contains(b)) {
+          __ load_aotrc_address(dest->as_register_lo(), b);
+          break;
+        }
+      }
       __ movptr(dest->as_register_lo(), (intptr_t)c->as_jlong());
 #else
       __ movptr(dest->as_register_lo(), c->as_jint_lo());
       __ movptr(dest->as_register_hi(), c->as_jint_hi());
 #endif // _LP64
@@ -2403,11 +2417,11 @@
             if (dest->as_xmm_double_reg() != value->as_xmm_double_reg()) {
               __ movdbl(dest->as_xmm_double_reg(), value->as_xmm_double_reg());
             }
             assert(!tmp->is_valid(), "do not need temporary");
             __ andpd(dest->as_xmm_double_reg(),
-                     ExternalAddress((address)double_signmask_pool),
+                     ExternalAddress(LIR_Assembler::double_signmask_pool),
                      rscratch1);
           }
         }
         break;
 
@@ -3810,11 +3824,11 @@
       assert(!tmp->is_valid(), "do not need temporary");
       if (left->as_xmm_float_reg() != dest->as_xmm_float_reg()) {
         __ movflt(dest->as_xmm_float_reg(), left->as_xmm_float_reg());
       }
       __ xorps(dest->as_xmm_float_reg(),
-               ExternalAddress((address)float_signflip_pool),
+               ExternalAddress(LIR_Assembler::float_signflip_pool),
                rscratch1);
     }
   } else if (dest->is_double_xmm()) {
 #ifdef _LP64
     if (UseAVX > 2 && !VM_Version::supports_avx512vl()) {
@@ -3828,11 +3842,11 @@
       assert(!tmp->is_valid(), "do not need temporary");
       if (left->as_xmm_double_reg() != dest->as_xmm_double_reg()) {
         __ movdbl(dest->as_xmm_double_reg(), left->as_xmm_double_reg());
       }
       __ xorpd(dest->as_xmm_double_reg(),
-               ExternalAddress((address)double_signflip_pool),
+               ExternalAddress(LIR_Assembler::double_signflip_pool),
                rscratch1);
     }
 #ifndef _LP64
   } else if (left->is_single_fpu() || left->is_double_fpu()) {
     assert(left->fpu() == 0, "arg must be on TOS");
diff a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp
@@ -51,10 +51,15 @@
     _deopt_handler_size = NOT_LP64(10) LP64_ONLY(17)
   };
 
 public:
 
+  static address  float_signmask_pool;
+  static address double_signmask_pool;
+  static address  float_signflip_pool;
+  static address double_signflip_pool;
+
   void store_parameter(Register r,  int offset_from_esp_in_words);
   void store_parameter(jint c,      int offset_from_esp_in_words);
   void store_parameter(jobject c,   int offset_from_esp_in_words);
   void store_parameter(Metadata* c, int offset_from_esp_in_words);
 
diff a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
@@ -3625,13 +3625,13 @@
   lea(ary1, Address(ary1, bound, Address::times(elsize)));
   subl(cnt1, bound);
   // release bound
 
   // vresult *= IntVector.fromArray(I256, power_of_31_backwards, 1);
+  lea(tmp2, ExternalAddress(StubRoutines::x86::arrays_hashcode_powers_of_31() + (0 * sizeof(jint))));
   for (int idx = 0; idx < 4; idx++) {
-    lea(tmp2, ExternalAddress(StubRoutines::x86::arrays_hashcode_powers_of_31() + ((8 * idx + 1) * sizeof(jint))));
-    arrays_hashcode_elvload(vcoef[idx], Address(tmp2, 0), T_INT);
+    arrays_hashcode_elvload(vcoef[idx], Address(tmp2, (int)((8 * idx + 1) * sizeof(jint))), T_INT);
     vpmulld(vresult[idx], vresult[idx], vcoef[idx], Assembler::AVX_256bit);
   }
   // result += vresult.reduceLanes(ADD);
   for (int idx = 0; idx < 4; idx++) {
     reduceI(Op_AddReductionVI, 256/(sizeof(jint) * 8), result, result, vresult[idx], vtmp[(idx * 2 + 0) % 4], vtmp[(idx * 2 + 1) % 4]);
diff a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp
@@ -22,10 +22,13 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "asm/macroAssembler.inline.hpp"
+#if INCLUDE_CDS
+#include "code/SCCache.hpp"
+#endif
 #include "gc/g1/g1BarrierSet.hpp"
 #include "gc/g1/g1BarrierSetAssembler.hpp"
 #include "gc/g1/g1BarrierSetRuntime.hpp"
 #include "gc/g1/g1CardTable.hpp"
 #include "gc/g1/g1HeapRegion.hpp"
@@ -281,34 +284,91 @@
   __ pop_call_clobbered_registers();
 
   __ bind(done);
 }
 
+// return a register that differs from reg1, reg2, reg3 and is not rcx
+
+static Register pick_different_reg(Register reg1, Register reg2 = noreg, Register reg3= noreg, Register reg4 = noreg) {
+  RegSet available = (RegSet::of(rscratch1, rscratch2, rax, rbx) + rdx -
+                      RegSet::of(reg1, reg2, reg3, reg4));
+  return *(available.begin());
+}
+
 static void generate_post_barrier_fast_path(MacroAssembler* masm,
                                             const Register store_addr,
                                             const Register new_val,
                                             const Register tmp,
                                             const Register tmp2,
                                             Label& done,
                                             bool new_val_may_be_null) {
   CardTableBarrierSet* ct = barrier_set_cast<CardTableBarrierSet>(BarrierSet::barrier_set());
   // Does store cross heap regions?
-  __ movptr(tmp, store_addr);                                    // tmp := store address
-  __ xorptr(tmp, new_val);                                       // tmp := store address ^ new value
-  __ shrptr(tmp, G1HeapRegion::LogOfHRGrainBytes);               // ((store address ^ new value) >> LogOfHRGrainBytes) == 0?
-  __ jcc(Assembler::equal, done);
+#if INCLUDE_CDS
+  // AOT code needs to load the barrier grain shift from the aot
+  // runtime constants area in the code cache otherwise we can compile
+  // it as an immediate operand
+
+  if (SCCache::is_on_for_write()) {
+    address grain_shift_addr = AOTRuntimeConstants::grain_shift_address();
+    Register save = pick_different_reg(rcx, tmp, new_val, store_addr);
+    __ push(save);
+    __ movptr(save, store_addr);
+    __ xorptr(save, new_val);
+    __ push(rcx);
+    __ lea(rcx, ExternalAddress(grain_shift_addr));
+    __ movptr(rcx, Address(rcx, 0));
+    __ shrptr(save);
+    __ pop(rcx);
+    __ mov(tmp, save);
+    __ pop(save);
+    __ jcc(Assembler::equal, done);
+  } else
+#endif // INCLUDE_CDS
+  {
+    __ movptr(tmp, store_addr);                                    // tmp := store address
+    __ xorptr(tmp, new_val);                                       // tmp := store address ^ new value
+    __ shrptr(tmp, G1HeapRegion::LogOfHRGrainBytes);               // ((store address ^ new value) >> LogOfHRGrainBytes) == 0?
+    __ jcc(Assembler::equal, done);
+  }
+
   // Crosses regions, storing null?
   if (new_val_may_be_null) {
     __ cmpptr(new_val, NULL_WORD);                               // new value == null?
     __ jcc(Assembler::equal, done);
   }
   // Storing region crossing non-null, is card young?
   __ movptr(tmp, store_addr);                                    // tmp := store address
-  __ shrptr(tmp, CardTable::card_shift());                       // tmp := card address relative to card table base
+#if INCLUDE_CDS
+  // AOT code needs to load the barrier card shift from the aot
+  // runtime constants area in the code cache otherwise we can compile
+  // it as an immediate operand
+  if (SCCache::is_on_for_write()) {
+    address card_shift_addr = AOTRuntimeConstants::card_shift_address();
+    Register save = pick_different_reg(rcx, tmp);
+    __ push(save);
+    __ mov(save, tmp);
+    __ push(rcx);
+    __ lea(rcx, ExternalAddress(card_shift_addr));
+    __ movptr(rcx, Address(rcx, 0));
+    __ shrptr(save);
+    __ pop(rcx);
+    __ mov(tmp, save);
+    __ pop(save);
+  } else
+#endif // INCLUDE_CDS
+  {
+    __ shrptr(tmp, CardTable::card_shift());                       // tmp := card address relative to card table base
+  }
   // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT
   // a valid address and therefore is not properly handled by the relocation code.
-  __ movptr(tmp2, (intptr_t)ct->card_table()->byte_map_base());  // tmp2 := card table base address
+  if (SCCache::is_on_for_write()) {
+    // SCA needs relocation info for this address
+    __ lea(tmp2, ExternalAddress((address)ct->card_table()->byte_map_base()));   // tmp2 := card table base address
+  } else {
+    __ movptr(tmp2, (intptr_t)ct->card_table()->byte_map_base());   // tmp2 := card table base address
+  }
   __ addptr(tmp, tmp2);                                          // tmp := card address
   __ cmpb(Address(tmp, 0), G1CardTable::g1_young_card_val());    // *(card address) == young_card_val?
 }
 
 static void generate_post_barrier_slow_path(MacroAssembler* masm,
@@ -655,11 +715,16 @@
 
   __ load_parameter(0, card_addr);
   __ shrptr(card_addr, CardTable::card_shift());
   // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT
   // a valid address and therefore is not properly handled by the relocation code.
-  __ movptr(cardtable, (intptr_t)ct->card_table()->byte_map_base());
+  if (SCCache::is_on()) {
+    // SCA needs relocation info for this address
+    __ lea(cardtable, ExternalAddress((address)ct->card_table()->byte_map_base()));
+  } else {
+    __ movptr(cardtable, (intptr_t)ct->card_table()->byte_map_base());
+  }
   __ addptr(card_addr, cardtable);
 
   NOT_LP64(__ get_thread(thread);)
 
   __ cmpb(Address(card_addr, 0), G1CardTable::g1_young_card_val());
diff a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "asm/macroAssembler.inline.hpp"
+#include "code/SCCache.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/cardTable.hpp"
 #include "gc/shared/cardTableBarrierSet.hpp"
 #include "gc/shared/cardTableBarrierSetAssembler.hpp"
 #include "gc/shared/gc_globals.hpp"
@@ -45,11 +46,11 @@
 void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
                                                                     Register addr, Register count, Register tmp) {
   BarrierSet *bs = BarrierSet::barrier_set();
   CardTableBarrierSet* ctbs = barrier_set_cast<CardTableBarrierSet>(bs);
   CardTable* ct = ctbs->card_table();
-  intptr_t disp = (intptr_t) ct->byte_map_base();
+  intptr_t byte_map_base = (intptr_t) ct->byte_map_base();
 
   Label L_loop, L_done;
   const Register end = count;
   assert_different_registers(addr, end);
 
@@ -62,11 +63,16 @@
   __ subptr(end, BytesPerHeapOop); // end - 1 to make inclusive
   __ shrptr(addr, CardTable::card_shift());
   __ shrptr(end, CardTable::card_shift());
   __ subptr(end, addr); // end --> cards count
 
-  __ mov64(tmp, disp);
+  if (SCCache::is_on_for_write()) {
+    // SCA needs relocation info for this address
+    __ lea(tmp, ExternalAddress((address)byte_map_base));
+  } else {
+    __ mov64(tmp, byte_map_base);
+  }
   __ addptr(addr, tmp);
 __ BIND(L_loop);
   __ movb(Address(addr, count, Address::times_1), 0);
   __ decrement(count);
   __ jcc(Assembler::greaterEqual, L_loop);
@@ -74,11 +80,11 @@
   __ lea(end,  Address(addr, count, Address::times_ptr, -wordSize));
   __ shrptr(addr, CardTable::card_shift());
   __ shrptr(end,   CardTable::card_shift());
   __ subptr(end, addr); // end --> count
 __ BIND(L_loop);
-  Address cardtable(addr, count, Address::times_1, disp);
+  Address cardtable(addr, count, Address::times_1, byte_map_base);
   __ movb(cardtable, 0);
   __ decrement(count);
   __ jcc(Assembler::greaterEqual, L_loop);
 #endif
 
@@ -101,10 +107,16 @@
   // byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift);
   // So this essentially converts an address to a displacement and it will
   // never need to be relocated. On 64bit however the value may be too
   // large for a 32bit displacement.
   intptr_t byte_map_base = (intptr_t)ct->byte_map_base();
+  if (SCCache::is_on_for_write()) {
+    // SCA needs relocation info for this address
+    ExternalAddress cardtable((address)byte_map_base);
+    Address index(noreg, obj, Address::times_1);
+    card_addr = __ as_Address(ArrayAddress(cardtable, index), rscratch1);
+  } else
   if (__ is_simm32(byte_map_base)) {
     card_addr = Address(noreg, obj, Address::times_1, byte_map_base);
   } else {
     // By doing it as an ExternalAddress 'byte_map_base' could be converted to a rip-relative
     // displacement and done in a single instruction given favorable mapping and a
diff a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp
--- a/src/hotspot/cpu/x86/interp_masm_x86.cpp
+++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp
@@ -38,10 +38,11 @@
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "runtime/basicLock.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/javaThread.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/safepointMechanism.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "utilities/powerOfTwo.hpp"
 
 // Implementation of InterpreterMacroAssembler
@@ -1987,10 +1988,21 @@
   if (where != nullptr) {
     jcc(Assembler::zero, *where);
   }
 }
 
+void InterpreterMacroAssembler::generate_runtime_upcalls_on_method_entry()
+{
+  address upcall = RuntimeUpcalls::on_method_entry_upcall_address();
+  if (RuntimeUpcalls::does_upcall_need_method_parameter(upcall)) {
+    get_method(c_rarg1);
+    call_VM(noreg,upcall, c_rarg1);
+  } else {
+    call_VM(noreg,upcall);
+  }
+}
+
 void InterpreterMacroAssembler::notify_method_entry() {
   // Whenever JVMTI is interp_only_mode, method entry/exit events are sent to
   // track stack depth.  If it is possible to enter interp_only_mode we add
   // the code to check if the event should be sent.
   Register rthread = LP64_ONLY(r15_thread) NOT_LP64(rcx);
diff a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp
--- a/src/hotspot/cpu/x86/interp_masm_x86.hpp
+++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp
@@ -276,10 +276,13 @@
   // only if +VerifyFPU  && (state == ftos || state == dtos)
   void verify_FPU(int stack_depth, TosState state = ftos);
 
   typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode;
 
+  // support for runtime upcalls
+  void generate_runtime_upcalls_on_method_entry();
+
   // support for jvmti/dtrace
   void notify_method_entry();
   void notify_method_exit(TosState state, NotifyMethodExitMode mode);
 
  private:
diff a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -23,10 +23,11 @@
  */
 
 #include "precompiled.hpp"
 #include "asm/assembler.hpp"
 #include "asm/assembler.inline.hpp"
+#include "code/SCCache.hpp"
 #include "code/compiledIC.hpp"
 #include "compiler/compiler_globals.hpp"
 #include "compiler/disassembler.hpp"
 #include "crc32c.h"
 #include "gc/shared/barrierSet.hpp"
@@ -769,10 +770,11 @@
   }
   lea(c_rarg0, ExternalAddress((address) msg));
   andq(rsp, -16); // align stack as required by ABI
   call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug64)));
   hlt();
+  SCCache::add_C_string(msg);
 }
 
 void MacroAssembler::warn(const char* msg) {
   push(rbp);
   movq(rbp, rsp);
@@ -10825,14 +10827,29 @@
   movq(rcx, Address(rsp, 14 * wordSize));
   movq(rax, Address(rsp, 15 * wordSize));
   addq(rsp, 16 * wordSize);
 }
 
+void MacroAssembler::load_aotrc_address(Register reg, address a) {
+#if INCLUDE_CDS
+  assert(AOTRuntimeConstants::contains(a), "address out of range for data area");
+  if (SCCache::is_on_for_write()) {
+    // all aotrc field addresses should be registered in the SCC address table
+    lea(reg, ExternalAddress(a));
+  } else {
+    mov64(reg, (uint64_t)a);
+  }
+#else
+  ShouldNotReachHere();
+#endif
+}
+
 void MacroAssembler::setcc(Assembler::Condition comparison, Register dst) {
   if (VM_Version::supports_apx_f()) {
     esetzucc(comparison, dst);
   } else {
     setb(comparison, dst);
     movzbl(dst, dst);
   }
 }
+
 #endif
diff a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
@@ -2240,10 +2240,11 @@
   void lightweight_unlock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow);
 
 #ifdef _LP64
   void save_legacy_gprs();
   void restore_legacy_gprs();
+  void load_aotrc_address(Register reg, address a);
   void setcc(Assembler::Condition comparison, Register dst);
 #endif
 };
 
 #endif // CPU_X86_MACROASSEMBLER_X86_HPP
diff a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp
--- a/src/hotspot/cpu/x86/methodHandles_x86.cpp
+++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp
@@ -109,16 +109,18 @@
   __ movl(temp, Address(member_reg, NONZERO(java_lang_invoke_MemberName::flags_offset())));
   __ shrl(temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT);
   __ andl(temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK);
   __ cmpl(temp, ref_kind);
   __ jcc(Assembler::equal, L);
-  { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal);
-    jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind);
+  { //char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal);
+    //jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind);
+    const char* buf = "verify_ref_kind"; // avoid allocating new strings
     if (ref_kind == JVM_REF_invokeVirtual ||
-        ref_kind == JVM_REF_invokeSpecial)
+        ref_kind == JVM_REF_invokeSpecial) {
       // could do this for all ref_kinds, but would explode assembly code size
       trace_method_handle(_masm, buf);
+    }
     __ STOP(buf);
   }
   BLOCK_COMMENT("} verify_ref_kind");
   __ bind(L);
 }
diff a/src/hotspot/cpu/x86/nativeInst_x86.hpp b/src/hotspot/cpu/x86/nativeInst_x86.hpp
--- a/src/hotspot/cpu/x86/nativeInst_x86.hpp
+++ b/src/hotspot/cpu/x86/nativeInst_x86.hpp
@@ -126,11 +126,11 @@
   address return_address() const            { return addr_at(return_address_offset); }
   address destination() const;
   void  set_destination(address dest)       {
 #ifdef AMD64
     intptr_t disp = dest - return_address();
-    guarantee(disp == (intptr_t)(jint)disp, "must be 32-bit offset");
+    guarantee(disp == (intptr_t)(jint)disp, "must be 32-bit offset: " INTPTR_FORMAT ", dest: " INTPTR_FORMAT ", ret_pc: " INTPTR_FORMAT, disp, p2i(dest), p2i(return_address()));
 #endif // AMD64
     set_int_at(displacement_offset, (int)(dest - return_address()));
   }
   // Returns whether the 4-byte displacement operand is 4-byte aligned.
   bool  is_displacement_aligned();
diff a/src/hotspot/cpu/x86/runtime_x86_64.cpp b/src/hotspot/cpu/x86/runtime_x86_64.cpp
--- a/src/hotspot/cpu/x86/runtime_x86_64.cpp
+++ b/src/hotspot/cpu/x86/runtime_x86_64.cpp
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #ifdef COMPILER2
 #include "asm/macroAssembler.hpp"
 #include "asm/macroAssembler.inline.hpp"
+#include "code/SCCache.hpp"
 #include "code/vmreg.hpp"
 #include "interpreter/interpreter.hpp"
 #include "opto/runtime.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
@@ -264,13 +265,21 @@
 
   // Allocate space for the code
   ResourceMark rm;
   // Setup code generation tools
   CodeBuffer buffer("exception_blob", 2048, 1024);
-  MacroAssembler* masm = new MacroAssembler(&buffer);
-
+  int pc_offset = 0;
+  if (SCCache::load_exception_blob(&buffer, &pc_offset)) {
+    OopMapSet* oop_maps = new OopMapSet();
+    oop_maps->add_gc_map(pc_offset, new OopMap(SimpleRuntimeFrame::framesize, 0));
+
+    // Set exception blob
+    _exception_blob =  ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
+    return;
+  }
 
+  MacroAssembler* masm = new MacroAssembler(&buffer);
   address start = __ pc();
 
   // Exception pc is 'return address' for stack walker
   __ push(rdx);
   __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Prolog
@@ -312,11 +321,12 @@
   // handle_exception_stub), since they were restored when we got the
   // exception.
 
   OopMapSet* oop_maps = new OopMapSet();
 
-  oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0));
+  pc_offset = the_pc - start;
+  oop_maps->add_gc_map(pc_offset, new OopMap(SimpleRuntimeFrame::framesize, 0));
 
   __ reset_last_Java_frame(false);
 
   // Restore callee-saved registers
 
@@ -353,9 +363,10 @@
   __ jmp(r8);
 
   // Make sure all code is generated
   masm->flush();
 
+  SCCache::store_exception_blob(&buffer, pc_offset);
   // Set exception blob
   _exception_blob =  ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
 }
 #endif // COMPILER2
diff a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #include "asm/macroAssembler.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/vmIntrinsics.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/barrierSetNMethod.hpp"
 #include "gc/shared/gc_globals.hpp"
@@ -3051,10 +3052,14 @@
 address StubGenerator::generate_multiplyToLen() {
   __ align(CodeEntryAlignment);
   StubCodeMark mark(this, "StubRoutines", "multiplyToLen");
   address start = __ pc();
 
+  if (SCCache::load_stub(this, vmIntrinsics::_multiplyToLen, "multiplyToLen", start)) {
+    return start;
+  }
+
   // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...)
   // Unix:  rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...)
   const Register x     = rdi;
   const Register xlen  = rax;
   const Register y     = rsi;
@@ -3087,10 +3092,11 @@
   restore_arg_regs();
 
   __ leave(); // required for proper stackwalking of RuntimeStub frame
   __ ret(0);
 
+  SCCache::store_stub(this, vmIntrinsics::_multiplyToLen, "multiplyToLen", start);
   return start;
 }
 
 /**
 *  Arguments:
@@ -3158,10 +3164,14 @@
 
   __ align(CodeEntryAlignment);
   StubCodeMark mark(this, "StubRoutines", "squareToLen");
   address start = __ pc();
 
+  if (SCCache::load_stub(this, vmIntrinsics::_squareToLen, "squareToLen", start)) {
+    return start;
+  }
+
   // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...)
   // Unix:  rdi, rsi, rdx, rcx (c_rarg0, c_rarg1, ...)
   const Register x      = rdi;
   const Register len    = rsi;
   const Register z      = r8;
@@ -3185,10 +3195,11 @@
   restore_arg_regs();
 
   __ leave(); // required for proper stackwalking of RuntimeStub frame
   __ ret(0);
 
+  SCCache::store_stub(this, vmIntrinsics::_squareToLen, "squareToLen", start);
   return start;
 }
 
 address StubGenerator::generate_method_entry_barrier() {
   __ align(CodeEntryAlignment);
@@ -3282,10 +3293,14 @@
 address StubGenerator::generate_mulAdd() {
   __ align(CodeEntryAlignment);
   StubCodeMark mark(this, "StubRoutines", "mulAdd");
   address start = __ pc();
 
+  if (SCCache::load_stub(this, vmIntrinsics::_mulAdd, "mulAdd", start)) {
+    return start;
+  }
+
   // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...)
   // Unix:  rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...)
   const Register out     = rdi;
   const Register in      = rsi;
   const Register offset  = r11;
@@ -3315,10 +3330,11 @@
   restore_arg_regs();
 
   __ leave(); // required for proper stackwalking of RuntimeStub frame
   __ ret(0);
 
+  SCCache::store_stub(this, vmIntrinsics::_mulAdd, "mulAdd", start);
   return start;
 }
 
 address StubGenerator::generate_bigIntegerRightShift() {
   __ align(CodeEntryAlignment);
diff a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp
--- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp
+++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp
@@ -808,11 +808,11 @@
 }
 
 // Interpreter stub for calling a native method. (asm interpreter)
 // This sets up a somewhat different looking stack for calling the
 // native method than the typical interpreter frame setup.
-address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_native_entry(bool synchronized, bool runtime_upcalls) {
   // determine code generation flags
   bool inc_counter  = UseCompiler || CountCompiledCalls;
 
   // rbx: Method*
   // rbcp: sender sp
@@ -934,10 +934,15 @@
 #endif
 
   // jvmti support
   __ notify_method_entry();
 
+  // runtime upcalls
+  if (runtime_upcalls) {
+    __ generate_runtime_upcalls_on_method_entry();
+  }
+
   // work registers
   const Register method = rbx;
   const Register thread = NOT_LP64(rdi) LP64_ONLY(r15_thread);
   const Register t      = NOT_LP64(rcx) LP64_ONLY(r11);
 
@@ -1372,11 +1377,11 @@
 }
 
 //
 // Generic interpreted method entry to (asm) interpreter
 //
-address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
+address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized, bool runtime_upcalls) {
   // determine code generation flags
   bool inc_counter  = UseCompiler || CountCompiledCalls;
 
   // ebx: Method*
   // rbcp: sender sp (set in InterpreterMacroAssembler::prepare_to_jump_from_interpreted / generate_call_stub)
@@ -1514,10 +1519,14 @@
 #endif
 
   // jvmti support
   __ notify_method_entry();
 
+  if (runtime_upcalls) {
+    __ generate_runtime_upcalls_on_method_entry();
+  }
+
   __ dispatch_next(vtos);
 
   // invocation counter overflow
   if (inc_counter) {
     // Handle overflow of counter and compile method
@@ -1830,10 +1839,22 @@
   generate_and_dispatch(t);
 }
 
 //-----------------------------------------------------------------------------
 
+void TemplateInterpreterGenerator::count_bytecode() {
+#ifdef _LP64
+  __ incrementq(ExternalAddress((address) &BytecodeCounter::_counter_value), rscratch1);
+#else
+  Unimplemented();
+#endif
+}
+
+void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
+  __ incrementl(ExternalAddress((address) &BytecodeHistogram::_counters[t->bytecode()]), rscratch1);
+}
+
 // Non-product code
 #ifndef PRODUCT
 
 address TemplateInterpreterGenerator::generate_trace_code(TosState state) {
   address entry = __ pc();
@@ -1872,18 +1893,10 @@
 #endif // _LP64
 
   return entry;
 }
 
-void TemplateInterpreterGenerator::count_bytecode() {
-  __ incrementl(ExternalAddress((address) &BytecodeCounter::_counter_value), rscratch1);
-}
-
-void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
-  __ incrementl(ExternalAddress((address) &BytecodeHistogram::_counters[t->bytecode()]), rscratch1);
-}
-
 void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) {
   __ mov32(rbx, ExternalAddress((address) &BytecodePairHistogram::_index));
   __ shrl(rbx, BytecodePairHistogram::log2_number_of_codes);
   __ orl(rbx,
          ((int) t->bytecode()) <<
diff a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp
@@ -2708,10 +2708,11 @@
                                             Register cache,
                                             Register index) {
   const Register temp = rbx;
   assert_different_registers(cache, index, temp);
 
+  Label L_clinit_barrier_slow;
   Label resolved;
 
   Bytecodes::Code code = bytecode();
   switch (code) {
     case Bytecodes::_nofast_getfield: code = Bytecodes::_getfield; break;
@@ -2728,17 +2729,29 @@
   }
   __ cmpl(temp, code);  // have we resolved this bytecode?
   __ jcc(Assembler::equal, resolved);
 
   // resolve first time through
+  __ bind(L_clinit_barrier_slow);
   address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache);
   __ movl(temp, code);
   __ call_VM(noreg, entry, temp);
   // Update registers with resolved info
   __ load_field_entry(cache, index);
 
   __ bind(resolved);
+
+  // Class initialization barrier for static fields
+  if (VM_Version::supports_fast_class_init_checks() &&
+      (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) {
+    const Register field_holder = temp;
+    const Register thread = LP64_ONLY(r15_thread) NOT_LP64(noreg);
+    assert(thread != noreg, "x86_32 not supported");
+
+    __ movptr(field_holder, Address(cache, in_bytes(ResolvedFieldEntry::field_holder_offset())));
+    __ clinit_barrier(field_holder, thread, nullptr /*L_fast_path*/, &L_clinit_barrier_slow);
+  }
 }
 
 void TemplateTable::load_resolved_field_entry(Register obj,
                                               Register cache,
                                               Register tos_state,
diff a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad
--- a/src/hotspot/cpu/x86/x86_64.ad
+++ b/src/hotspot/cpu/x86/x86_64.ad
@@ -424,11 +424,10 @@
 
 %}
 
 // Register masks
 source_hpp %{
-
 extern RegMask _ANY_REG_mask;
 extern RegMask _PTR_REG_mask;
 extern RegMask _PTR_REG_NO_RBP_mask;
 extern RegMask _PTR_NO_RAX_REG_mask;
 extern RegMask _PTR_NO_RAX_RBX_REG_mask;
@@ -837,11 +836,11 @@
   int framesize = C->output()->frame_size_in_bytes();
   int bangsize = C->output()->bang_size_in_bytes();
 
   if (C->clinit_barrier_on_entry()) {
     assert(VM_Version::supports_fast_class_init_checks(), "sanity");
-    assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started");
+    assert(!C->method()->holder()->is_not_initialized() || C->do_clinit_barriers(), "initialization should have been started");
 
     Label L_skip_barrier;
     Register klass = rscratch1;
 
     __ mov_metadata(klass, C->method()->holder()->constant_encoding());
@@ -1845,11 +1844,16 @@
     assert(off1 - off0 == clear_avx_size(), "correct size prediction");
   %}
 
   enc_class Java_To_Runtime(method meth) %{
     // No relocation needed
-    __ mov64(r10, (int64_t) $meth$$method);
+    if (SCCache::is_on_for_write()) {
+      // Created runtime_call_type relocation when caching code
+      __ lea(r10, RuntimeAddress((address)$meth$$method));
+    } else {
+      __ mov64(r10, (int64_t) $meth$$method);
+    }
     __ call(r10);
     __ post_call_nop();
   %}
 
   enc_class Java_Static_Call(method meth)
@@ -2462,10 +2466,22 @@
 
   format %{ %}
   interface(CONST_INTER);
 %}
 
+// AOT Runtime Constants Address
+operand immAOTRuntimeConstantsAddress()
+%{
+  // Check if the address is in the range of AOT Runtime Constants
+  predicate(AOTRuntimeConstants::contains((address)(n->get_ptr())));
+  match(ConP);
+
+  op_cost(0);
+  format %{ %}
+  interface(CONST_INTER);
+%}
+
 operand kReg()
 %{
   constraint(ALLOC_IN_RC(vectmask_reg));
   match(RegVectMask);
   format %{%}
@@ -4417,10 +4433,23 @@
     __ movdbl($dst$$XMMRegister, $mem$$Address);
   %}
   ins_pipe(pipe_slow); // XXX
 %}
 
+instruct loadAOTRCAddress(rRegP dst, immAOTRuntimeConstantsAddress con)
+%{
+  match(Set dst con);
+
+  format %{ "leaq  $dst, $con\t# AOT Runtime Constants Address" %}
+
+  ins_encode %{
+    __ load_aotrc_address($dst$$Register, (address)$con$$constant);
+  %}
+
+  ins_pipe(ialu_reg_fat);
+%}
+
 // max = java.lang.Math.max(float a, float b)
 instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{
   predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n));
   match(Set dst (MaxF a b));
   effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp);
diff a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
--- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
+++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
@@ -270,15 +270,12 @@
             next_pc = UnsafeMemoryAccess::page_error_continue_pc(pc);
           }
           stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
         }
       } else if (sig == SIGILL && nativeInstruction_at(pc)->is_stop()) {
-        // Pull a pointer to the error message out of the instruction
-        // stream.
-        const uint64_t *detail_msg_ptr
-          = (uint64_t*)(pc + NativeInstruction::instruction_size);
-        const char *detail_msg = (const char *)*detail_msg_ptr;
+        // A pointer to the message will have been placed in x0
+        const char *detail_msg = (const char *)(uc->uc_mcontext->DU3_PREFIX(ss,x[0]));
         const char *msg = "stop";
         if (TraceTraps) {
           tty->print_cr("trap: %s: (SIGILL)", msg);
         }
 
diff a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
--- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
+++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
@@ -247,15 +247,12 @@
             next_pc = UnsafeMemoryAccess::page_error_continue_pc(pc);
           }
           stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
         }
       } else if (sig == SIGILL && nativeInstruction_at(pc)->is_stop()) {
-        // Pull a pointer to the error message out of the instruction
-        // stream.
-        const uint64_t *detail_msg_ptr
-          = (uint64_t*)(pc + NativeInstruction::instruction_size);
-        const char *detail_msg = (const char *)*detail_msg_ptr;
+        // A pointer to the message will have been placed in r0
+        const char *detail_msg = (const char *)(uc->uc_mcontext.regs[0]);
         const char *msg = "stop";
         if (TraceTraps) {
           tty->print_cr("trap: %s: (SIGILL)", msg);
         }
 
diff a/src/hotspot/share/adlc/main.cpp b/src/hotspot/share/adlc/main.cpp
--- a/src/hotspot/share/adlc/main.cpp
+++ b/src/hotspot/share/adlc/main.cpp
@@ -216,10 +216,11 @@
   AD.addInclude(AD._CPP_file, "memory/allocation.inline.hpp");
   AD.addInclude(AD._CPP_file, "code/codeCache.hpp");
   AD.addInclude(AD._CPP_file, "code/compiledIC.hpp");
   AD.addInclude(AD._CPP_file, "code/nativeInst.hpp");
   AD.addInclude(AD._CPP_file, "code/vmreg.inline.hpp");
+  AD.addInclude(AD._CPP_file, "code/SCCache.hpp");
   AD.addInclude(AD._CPP_file, "gc/shared/collectedHeap.inline.hpp");
   AD.addInclude(AD._CPP_file, "oops/compressedOops.hpp");
   AD.addInclude(AD._CPP_file, "oops/markWord.hpp");
   AD.addInclude(AD._CPP_file, "oops/method.hpp");
   AD.addInclude(AD._CPP_file, "oops/oop.inline.hpp");
@@ -264,10 +265,11 @@
   AD.addInclude(AD._CPP_PEEPHOLE_file, "adfiles", get_basename(AD._HPP_file._name));
   AD.addInclude(AD._CPP_PIPELINE_file, "precompiled.hpp");
   AD.addInclude(AD._CPP_PIPELINE_file, "adfiles", get_basename(AD._HPP_file._name));
   AD.addInclude(AD._DFA_file, "precompiled.hpp");
   AD.addInclude(AD._DFA_file, "adfiles", get_basename(AD._HPP_file._name));
+  AD.addInclude(AD._DFA_file, "code/SCCache.hpp");
   AD.addInclude(AD._DFA_file, "oops/compressedOops.hpp");
   AD.addInclude(AD._DFA_file, "opto/cfgnode.hpp");  // Use PROB_MAX in predicate.
   AD.addInclude(AD._DFA_file, "opto/intrinsicnode.hpp");
   AD.addInclude(AD._DFA_file, "opto/matcher.hpp");
   AD.addInclude(AD._DFA_file, "opto/narrowptrnode.hpp");
diff a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp
--- a/src/hotspot/share/asm/codeBuffer.cpp
+++ b/src/hotspot/share/asm/codeBuffer.cpp
@@ -27,10 +27,11 @@
 #include "code/compiledIC.hpp"
 #include "code/oopRecorder.inline.hpp"
 #include "compiler/disassembler.hpp"
 #include "logging/log.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/methodCounters.hpp"
 #include "oops/methodData.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/icache.hpp"
 #include "runtime/safepointVerifiers.hpp"
 #include "utilities/align.hpp"
@@ -535,10 +536,13 @@
           Metadata* m = md->metadata_value();
           if (oop_recorder()->is_real(m)) {
             if (m->is_methodData()) {
               m = ((MethodData*)m)->method();
             }
+            if (m->is_methodCounters()) {
+              m = ((MethodCounters*)m)->method();
+            }
             if (m->is_method()) {
               m = ((Method*)m)->method_holder();
             }
             if (m->is_klass()) {
               append_oop_references(&oops, (Klass*)m);
@@ -559,10 +563,13 @@
       Metadata* m = oop_recorder()->metadata_at(i);
       if (oop_recorder()->is_real(m)) {
         if (m->is_methodData()) {
           m = ((MethodData*)m)->method();
         }
+        if (m->is_methodCounters()) {
+          m = ((MethodCounters*)m)->method();
+        }
         if (m->is_method()) {
           m = ((Method*)m)->method_holder();
         }
         if (m->is_klass()) {
           append_oop_references(&oops, (Klass*)m);
@@ -721,11 +728,11 @@
 
 void CodeBuffer::copy_code_to(CodeBlob* dest_blob) {
 #ifndef PRODUCT
   if (PrintNMethods && (WizardMode || Verbose)) {
     tty->print("done with CodeBuffer:");
-    ((CodeBuffer*)this)->print();
+    ((CodeBuffer*)this)->print_on(tty);
   }
 #endif //PRODUCT
 
   CodeBuffer dest(dest_blob);
   assert(dest_blob->content_size() >= total_content_size(), "good sizing");
@@ -860,11 +867,11 @@
 
 void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) {
 #ifndef PRODUCT
   if (PrintNMethods && (WizardMode || Verbose)) {
     tty->print("expanding CodeBuffer:");
-    this->print();
+    this->print_on(tty);
   }
 
   if (StressCodeBuffers && blob() != nullptr) {
     static int expand_count = 0;
     if (expand_count >= 0)  expand_count += 1;
@@ -947,11 +954,11 @@
 
 #ifndef PRODUCT
   _decode_begin = nullptr;  // sanity
   if (PrintNMethods && (WizardMode || Verbose)) {
     tty->print("expanded CodeBuffer:");
-    this->print();
+    this->print_on(tty);
   }
 #endif //PRODUCT
 }
 
 void CodeBuffer::adjust_internal_address(address from, address to) {
@@ -999,27 +1006,27 @@
       if (!other->is_allocated() || other == sect) {
         continue;
       }
       guarantee(other->disjoint(sect), "sanity");
     }
-    guarantee(sect->end() <= tend, "sanity");
-    guarantee(sect->end() <= sect->limit(), "sanity");
+    guarantee(sect->end() <= tend, "sanity, sect_end: " PTR_FORMAT " tend: " PTR_FORMAT " size: %d", p2i(sect->end()), p2i(tend), (int)_total_size);
+    guarantee(sect->end() <= sect->limit(), "sanity, sect_end: " PTR_FORMAT " sect_limit: " PTR_FORMAT, p2i(sect->end()), p2i(sect->limit()));
   }
 }
 
 void CodeBuffer::log_section_sizes(const char* name) {
   if (xtty != nullptr) {
     ttyLocker ttyl;
     // log info about buffer usage
-    xtty->print_cr("<blob name='%s' total_size='%d'>", name, _total_size);
+    xtty->head("blob name='%s' total_size='%d'", name, _total_size);
     for (int n = (int) CodeBuffer::SECT_FIRST; n < (int) CodeBuffer::SECT_LIMIT; n++) {
       CodeSection* sect = code_section(n);
       if (!sect->is_allocated() || sect->is_empty())  continue;
-      xtty->print_cr("<sect index='%d' capacity='%d' size='%d' remaining='%d'/>",
-                     n, sect->capacity(), sect->size(), sect->remaining());
+      xtty->elem("sect index='%d' capacity='%d' size='%d' remaining='%d'",
+                 n, sect->capacity(), sect->size(), sect->remaining());
     }
-    xtty->print_cr("</blob>");
+    xtty->tail("blob");
   }
 }
 
 bool CodeBuffer::finalize_stubs() {
   if (_finalize_stubs && !pd_finalize_stubs()) {
@@ -1057,28 +1064,35 @@
   ttyLocker ttyl;
   Disassembler::decode(decode_begin(), insts_end(), tty NOT_PRODUCT(COMMA &asm_remarks()));
   _decode_begin = insts_end();
 }
 
-void CodeSection::print(const char* name) {
+void CodeSection::print_on(outputStream* st, const char* name) {
   csize_t locs_size = locs_end() - locs_start();
-  tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)",
-                name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity());
-  tty->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d",
-                name, p2i(locs_start()), p2i(locs_end()), p2i(locs_limit()), locs_size, locs_capacity(), locs_point_off());
+  st->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)",
+               name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity());
+  st->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d",
+               name, p2i(locs_start()), p2i(locs_end()), p2i(locs_limit()), locs_size, locs_capacity(), locs_point_off());
   if (PrintRelocations && (locs_size != 0)) {
     RelocIterator iter(this);
-    iter.print();
+    iter.print_on(st);
   }
 }
 
-void CodeBuffer::print() {
-  tty->print_cr("CodeBuffer:");
+void CodeBuffer::print_on(outputStream* st) {
+#if 0
+  if (this == nullptr) { // gcc complains 'nonnull' argument 'this' compared to NULL 
+    st->print_cr("null CodeBuffer pointer");
+    return;
+  }
+#endif
+
+  st->print_cr("CodeBuffer:%s", name());
   for (int n = 0; n < (int)SECT_LIMIT; n++) {
     // print each section
     CodeSection* cs = code_section(n);
-    cs->print(code_section_name(n));
+    cs->print_on(st, code_section_name(n));
   }
 }
 
 // ----- CHeapString -----------------------------------------------------------
 
diff a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp
--- a/src/hotspot/share/asm/codeBuffer.hpp
+++ b/src/hotspot/share/asm/codeBuffer.hpp
@@ -87,10 +87,11 @@
 // This class represents a stream of code and associated relocations.
 // There are a few in each CodeBuffer.
 // They are filled concurrently, and concatenated at the end.
 class CodeSection {
   friend class CodeBuffer;
+  friend class SCCReader;
  public:
   typedef int csize_t;  // code size type; would be size_t except for history
 
  private:
   address     _start;           // first byte of contents (instructions)
@@ -281,11 +282,11 @@
   // Return true if there was an expansion.
   bool maybe_expand_to_ensure_remaining(csize_t amount);
 
 #ifndef PRODUCT
   void decode();
-  void print(const char* name);
+  void print_on(outputStream* st, const char* name);
 #endif //PRODUCT
 };
 
 
 #ifndef PRODUCT
@@ -384,10 +385,11 @@
 // addresses in a sibling section.
 
 class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) {
   friend class CodeSection;
   friend class StubCodeGenerator;
+  friend class SCCReader;
 
  private:
   // CodeBuffers must be allocated on the stack except for a single
   // special case during expansion which is handled internally.  This
   // is done to guarantee proper cleanup of resources.
@@ -739,11 +741,11 @@
 #ifndef PRODUCT
  public:
   // Printing / Decoding
   // decodes from decode_begin() to code_end() and sets decode_begin to end
   void    decode();
-  void    print();
+  void    print_on(outputStream* st);
 #endif
   // Directly disassemble code buffer.
   void    decode(address start, address end);
 
   // The following header contains architecture-specific implementations
diff a/src/hotspot/share/c1/c1_Compilation.cpp b/src/hotspot/share/c1/c1_Compilation.cpp
--- a/src/hotspot/share/c1/c1_Compilation.cpp
+++ b/src/hotspot/share/c1/c1_Compilation.cpp
@@ -430,15 +430,18 @@
     in_bytes(frame_map()->framesize_in_bytes()) / sizeof(intptr_t),
     debug_info_recorder()->_oopmaps,
     exception_handler_table(),
     implicit_exception_table(),
     compiler(),
+    false, // has_clinit_barriers
+    false, // for_preload
     has_unsafe_access(),
     SharedRuntime::is_wide_vector(max_vector_size()),
     has_monitors(),
     has_scoped_access(),
-    _immediate_oops_patched
+    _immediate_oops_patched,
+    should_install_code()
   );
 }
 
 
 void Compilation::compile_method() {
@@ -479,12 +482,11 @@
 
   // bailout if method couldn't be compiled
   // Note: make sure we mark the method as not compilable!
   CHECK_BAILOUT();
 
-  if (should_install_code()) {
-    // install code
+  { // install code
     PhaseTraceTime timeit(_t_codeinstall);
     install_code(frame_size);
   }
 
   if (log() != nullptr) // Print code cache state into compiler log
diff a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp
--- a/src/hotspot/share/c1/c1_Compiler.cpp
+++ b/src/hotspot/share/c1/c1_Compiler.cpp
@@ -29,10 +29,11 @@
 #include "c1/c1_GraphBuilder.hpp"
 #include "c1/c1_LinearScan.hpp"
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "c1/c1_ValueType.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compilerDirectives.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "jfr/support/jfrIntrinsics.hpp"
 #include "memory/allocation.hpp"
@@ -49,10 +50,11 @@
 }
 
 void Compiler::init_c1_runtime() {
   BufferBlob* buffer_blob = CompilerThread::current()->get_buffer_blob();
   Runtime1::initialize(buffer_blob);
+  SCCache::init_c1_table();
   FrameMap::initialize();
   // initialize data structures
   ValueType::initialize();
   GraphBuilder::initialize();
   // note: to use more than one instance of LinearScan at a time this function call has to
@@ -250,10 +252,27 @@
 
   return true;
 }
 
 void Compiler::compile_method(ciEnv* env, ciMethod* method, int entry_bci, bool install_code, DirectiveSet* directive) {
+  CompileTask* task = env->task();
+  if (install_code && task->is_scc()) {
+    assert(!task->preload(), "Pre-loading cached code is not implemeted for C1 code");
+    bool success = SCCache::load_nmethod(env, method, entry_bci, this, CompLevel(task->comp_level()));
+    if (success) {
+      assert(task->is_success(), "sanity");
+      return;
+    }
+    SCCache::invalidate(task->scc_entry()); // mark scc_entry as not entrant
+    if (SCCache::is_code_load_thread_on() && !StoreCachedCode) {
+      // Bail out if failed to load cached code in SC thread
+      // unless the code is updating.
+      env->record_failure("Failed to load cached code");
+      return;
+    }
+    task->clear_scc();
+  }
   BufferBlob* buffer_blob = CompilerThread::current()->get_buffer_blob();
   assert(buffer_blob != nullptr, "Must exist");
   // invoke compilation
   {
     // We are nested here because we need for the destructor
diff a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -40,10 +40,11 @@
 #include "compiler/compilerEvent.hpp"
 #include "interpreter/bytecode.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/oop.inline.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/bitMap.inline.hpp"
 #include "utilities/checkedCast.hpp"
 #include "utilities/powerOfTwo.hpp"
@@ -4077,10 +4078,18 @@
     Values* args = new Values(1);
     args->push(append(new Constant(new MethodConstant(method()))));
     append(new RuntimeCall(voidType, "dtrace_method_entry", CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), args));
   }
 
+  MethodDetails method_details(callee);
+  RuntimeUpcallInfo* upcall = RuntimeUpcalls::get_first_upcall(RuntimeUpcallType::onMethodEntry, method_details);
+  while (upcall != nullptr) {
+    Values* args = new Values(0);
+    append(new RuntimeCall(voidType, upcall->upcall_name(), upcall->upcall_address(), args));
+    upcall = RuntimeUpcalls::get_next_upcall(RuntimeUpcallType::onMethodEntry, method_details, upcall);
+  }
+
   if (profile_inlined_calls()) {
     profile_invocation(callee, copy_state_before_with_bci(SynchronizationEntryBCI));
   }
 
   BlockBegin* callee_start_block = block_at(0);
diff a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp
--- a/src/hotspot/share/c1/c1_LIRGenerator.cpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp
@@ -32,16 +32,18 @@
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArrayKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciObjArray.hpp"
 #include "ci/ciUtilities.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/compilerOracle.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/c1/barrierSetC1.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/methodCounters.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/bitMap.inline.hpp"
 #include "utilities/macros.hpp"
@@ -2673,10 +2675,21 @@
     __ metadata2reg(method()->constant_encoding(), meth);
     args->append(meth);
     call_runtime(&signature, args, CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), voidType, nullptr);
   }
 
+  MethodDetails method_details(method());
+  RuntimeUpcallInfo* upcall = RuntimeUpcalls::get_first_upcall(RuntimeUpcallType::onMethodEntry, method_details);
+  while (upcall != nullptr) {
+    BasicTypeList signature;
+    signature.append(LP64_ONLY(T_LONG) NOT_LP64(T_INT));    // thread
+    LIR_OprList* args = new LIR_OprList();
+    args->append(getThreadPointer());
+    call_runtime(&signature, args, upcall->upcall_address(), voidType, nullptr);
+    upcall = RuntimeUpcalls::get_next_upcall(RuntimeUpcallType::onMethodEntry, method_details, upcall);
+  }
+
   if (method()->is_synchronized()) {
     LIR_Opr obj;
     if (method()->is_static()) {
       obj = new_register(T_OBJECT);
       __ oop2reg(method()->holder()->java_mirror()->constant_encoding(), obj);
@@ -3278,12 +3291,17 @@
     MethodCounters* counters_adr = method->ensure_method_counters();
     if (counters_adr == nullptr) {
       bailout("method counters allocation failed");
       return;
     }
+if (SCCache::is_on()) {
+    counter_holder = new_register(T_METADATA);
+    __ metadata2reg(counters_adr, counter_holder);
+} else {
     counter_holder = new_pointer_register();
     __ move(LIR_OprFact::intptrConst(counters_adr), counter_holder);
+}
     offset = in_bytes(backedge ? MethodCounters::backedge_counter_offset() :
                                  MethodCounters::invocation_counter_offset());
   } else if (level == CompLevel_full_profile) {
     counter_holder = new_register(T_METADATA);
     offset = in_bytes(backedge ? MethodData::backedge_counter_offset() :
diff a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
--- a/src/hotspot/share/c1/c1_Runtime1.cpp
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp
@@ -37,10 +37,11 @@
 #include "code/compiledIC.hpp"
 #include "code/pcDesc.hpp"
 #include "code/scopeDesc.hpp"
 #include "code/vtableStubs.hpp"
 #include "compiler/compilationPolicy.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/disassembler.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/c1/barrierSetC1.hpp"
 #include "gc/shared/collectedHeap.hpp"
@@ -62,17 +63,20 @@
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/javaCalls.hpp"
+#include "runtime/perfData.inline.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stackWatermarkSet.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/threadCritical.hpp"
 #include "runtime/vframe.inline.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vm_version.hpp"
+#include "services/management.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/events.hpp"
 
 
 // Implementation of StubAssembler
@@ -263,10 +267,11 @@
   // install blob
   _blobs[(int)id] = blob;
 }
 
 void Runtime1::initialize(BufferBlob* blob) {
+  init_counters();
   // platform-dependent initialization
   initialize_pd();
   // generate stubs
   int limit = (int)C1StubId::NUM_STUBIDS;
   for (int id = 0; id < limit; id++) generate_blob_for(blob, (C1StubId)id);
@@ -349,16 +354,22 @@
   FUNCTION_CASE(entry, StubRoutines::dtan());
   FUNCTION_CASE(entry, StubRoutines::dtanh());
 
 #undef FUNCTION_CASE
 
+  // Runtime upcalls also has a map of addresses to names
+  const char* upcall_name = RuntimeUpcalls::get_name_for_upcall_address(entry);
+  if (upcall_name != nullptr) {
+    return upcall_name;
+  }
+
   // Soft float adds more runtime names.
   return pd_name_for_address(entry);
 }
 
 
-JRT_ENTRY(void, Runtime1::new_instance(JavaThread* current, Klass* klass))
+JRT_ENTRY_PROF(void, Runtime1, new_instance, Runtime1::new_instance(JavaThread* current, Klass* klass))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _new_instance_slowcase_cnt++;
   }
 #endif
@@ -372,11 +383,11 @@
   oop obj = h->allocate_instance(CHECK);
   current->set_vm_result(obj);
 JRT_END
 
 
-JRT_ENTRY(void, Runtime1::new_type_array(JavaThread* current, Klass* klass, jint length))
+JRT_ENTRY_PROF(void, Runtime1, new_type_array, Runtime1::new_type_array(JavaThread* current, Klass* klass, jint length))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _new_type_array_slowcase_cnt++;
   }
 #endif
@@ -394,11 +405,11 @@
   }
 
 JRT_END
 
 
-JRT_ENTRY(void, Runtime1::new_object_array(JavaThread* current, Klass* array_klass, jint length))
+JRT_ENTRY_PROF(void, Runtime1, new_object_array, Runtime1::new_object_array(JavaThread* current, Klass* array_klass, jint length))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _new_object_array_slowcase_cnt++;
   }
 #endif
@@ -416,11 +427,11 @@
     deopt_caller(current);
   }
 JRT_END
 
 
-JRT_ENTRY(void, Runtime1::new_multi_array(JavaThread* current, Klass* klass, int rank, jint* dims))
+JRT_ENTRY_PROF(void, Runtime1, new_multi_array, Runtime1::new_multi_array(JavaThread* current, Klass* klass, int rank, jint* dims))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _new_multi_array_slowcase_cnt++;
   }
 #endif
@@ -487,11 +498,11 @@
   }
   osr_nm = CompilationPolicy::event(enclosing_method, method, branch_bci, bci, level, nm, current);
   return osr_nm;
 }
 
-JRT_BLOCK_ENTRY(address, Runtime1::counter_overflow(JavaThread* current, int bci, Method* method))
+JRT_BLOCK_ENTRY_PROF(address, Runtime1, counter_overflow, Runtime1::counter_overflow(JavaThread* current, int bci, Method* method))
   nmethod* osr_nm;
   JRT_BLOCK
     osr_nm = counter_overflow_helper(current, bci, method);
     if (osr_nm != nullptr) {
       RegisterMap map(current,
@@ -521,11 +532,11 @@
 // control the area where we can allow a safepoint. After we exit the safepoint area we can
 // check to see if the handler we are going to return is now in a nmethod that has
 // been deoptimized. If that is the case we return the deopt blob
 // unpack_with_exception entry instead. This makes life for the exception blob easier
 // because making that same check and diverting is painful from assembly language.
-JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* current, oopDesc* ex, address pc, nmethod*& nm))
+JRT_ENTRY_NO_ASYNC_PROF(static address, Runtime1, exception_handler_for_pc_helper, exception_handler_for_pc_helper(JavaThread* current, oopDesc* ex, address pc, nmethod*& nm))
   // Reset method handle flag.
   current->set_is_method_handle_return(false);
 
   Handle exception(current, ex);
 
@@ -753,11 +764,11 @@
   ResourceMark rm(current);
   SharedRuntime::throw_and_post_jvmti_exception(current, vmSymbols::java_lang_IncompatibleClassChangeError());
 JRT_END
 
 
-JRT_BLOCK_ENTRY(void, Runtime1::monitorenter(JavaThread* current, oopDesc* obj, BasicObjectLock* lock))
+JRT_BLOCK_ENTRY_PROF(void, Runtime1, monitorenter, Runtime1::monitorenter(JavaThread* current, oopDesc* obj, BasicObjectLock* lock))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _monitorenter_slowcase_cnt++;
   }
 #endif
@@ -767,11 +778,11 @@
   assert(obj == lock->obj(), "must match");
   SharedRuntime::monitor_enter_helper(obj, lock->lock(), current);
 JRT_END
 
 
-JRT_LEAF(void, Runtime1::monitorexit(JavaThread* current, BasicObjectLock* lock))
+JRT_LEAF_PROF(void, Runtime1, monitorexit, Runtime1::monitorexit(JavaThread* current, BasicObjectLock* lock))
   assert(current == JavaThread::current(), "pre-condition");
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _monitorexit_slowcase_cnt++;
   }
@@ -781,11 +792,11 @@
   assert(oopDesc::is_oop(obj), "must be null or an object");
   SharedRuntime::monitor_exit_helper(obj, lock->lock(), current);
 JRT_END
 
 // Cf. OptoRuntime::deoptimize_caller_frame
-JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* current, jint trap_request))
+JRT_ENTRY_PROF(void, Runtime1, deoptimize, Runtime1::deoptimize(JavaThread* current, jint trap_request))
   // Called from within the owner thread, so no need for safepoint
   RegisterMap reg_map(current,
                       RegisterMap::UpdateMap::skip,
                       RegisterMap::ProcessFrames::include,
                       RegisterMap::WalkContinuation::skip);
@@ -824,11 +835,12 @@
   Bytecodes::Code code       = field_access.code();
 
   // We must load class, initialize class and resolve the field
   fieldDescriptor result; // initialize class if needed
   constantPoolHandle constants(THREAD, caller->constants());
-  LinkResolver::resolve_field_access(result, constants, field_access.index(), caller, Bytecodes::java_code(code), CHECK_NULL);
+  LinkResolver::resolve_field_access(result, constants, field_access.index(), caller,
+                                     Bytecodes::java_code(code), true /*initialize_class*/, CHECK_NULL);
   return result.field_holder();
 }
 
 
 //
@@ -927,11 +939,11 @@
 // but we don't want to bet the farm on this always working.
 //
 // Therefore, if there is any chance of a race condition, we try to
 // patch only naturally aligned words, as single, full-word writes.
 
-JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, C1StubId stub_id ))
+JRT_ENTRY_PROF(void, Runtime1, patch_code, Runtime1::patch_code(JavaThread* current, C1StubId stub_id))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _patch_code_slowcase_cnt++;
   }
 #endif
@@ -972,11 +984,12 @@
 
     Bytecode_field field_access(caller_method, bci);
     fieldDescriptor result; // initialize class if needed
     Bytecodes::Code code = field_access.code();
     constantPoolHandle constants(current, caller_method->constants());
-    LinkResolver::resolve_field_access(result, constants, field_access.index(), caller_method, Bytecodes::java_code(code), CHECK);
+    LinkResolver::resolve_field_access(result, constants, field_access.index(), caller_method,
+                                       Bytecodes::java_code(code), true /*initialize_class*/, CHECK);
     patch_field_offset = result.offset();
 
     // If we're patching a field which is volatile then at compile it
     // must not have been know to be volatile, so the generated code
     // isn't correct for a volatile reference.  The nmethod has to be
@@ -1310,11 +1323,11 @@
     }
   }
   return true;
 }
 
-void Runtime1::patch_code(JavaThread* current, C1StubId stub_id) {
+PROF_ENTRY(void, Runtime1, patch_code, Runtime1::patch_code(JavaThread* current, C1StubId stub_id))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _patch_code_slowcase_cnt++;
   }
 #endif
@@ -1345,11 +1358,11 @@
   }
 
   Deoptimization::deoptimize_frame(current, caller_frame.id());
   // Return to the now deoptimized frame.
   postcond(caller_is_deopted(current));
-}
+PROF_END
 
 #endif // DEOPTIMIZE_WHEN_PATCHING
 
 // Entry point for compiled code. We want to patch a nmethod.
 // We don't do a normal VM transition here because we want to
@@ -1436,17 +1449,17 @@
 
   return caller_is_deopted(current);
 }
 
 
-JRT_LEAF(void, Runtime1::trace_block_entry(jint block_id))
+JRT_LEAF_PROF_NO_THREAD(void, Runtime1, trace_block_entry, Runtime1::trace_block_entry(jint block_id))
   // for now we just print out the block id
   tty->print("%d ", block_id);
 JRT_END
 
 
-JRT_LEAF(int, Runtime1::is_instance_of(oopDesc* mirror, oopDesc* obj))
+JRT_LEAF_PROF_NO_THREAD(int, Runtime1, is_instance_of, Runtime1::is_instance_of(oopDesc* mirror, oopDesc* obj))
   // had to return int instead of bool, otherwise there may be a mismatch
   // between the C calling convention and the Java one.
   // e.g., on x86, GCC may clear only %al when returning a bool false, but
   // JVM takes the whole %eax as the return value, which may misinterpret
   // the return value as a boolean true.
@@ -1454,11 +1467,11 @@
   assert(mirror != nullptr, "should null-check on mirror before calling");
   Klass* k = java_lang_Class::as_Klass(mirror);
   return (k != nullptr && obj != nullptr && obj->is_a(k)) ? 1 : 0;
 JRT_END
 
-JRT_ENTRY(void, Runtime1::predicate_failed_trap(JavaThread* current))
+JRT_ENTRY_PROF(void, Runtime1, predicate_failed_trap, Runtime1::predicate_failed_trap(JavaThread* current))
   ResourceMark rm;
 
   RegisterMap reg_map(current,
                       RegisterMap::UpdateMap::skip,
                       RegisterMap::ProcessFrames::include,
@@ -1514,43 +1527,99 @@
     }
   }
   Exceptions::debug_check_abort(ex->klass()->external_name(), message);
 JRT_END
 
+#define DO_COUNTERS(macro) \
+  macro(Runtime1, new_instance) \
+  macro(Runtime1, new_type_array) \
+  macro(Runtime1, new_object_array) \
+  macro(Runtime1, new_multi_array) \
+  macro(Runtime1, counter_overflow) \
+  macro(Runtime1, exception_handler_for_pc_helper) \
+  macro(Runtime1, monitorenter) \
+  macro(Runtime1, monitorexit) \
+  macro(Runtime1, deoptimize) \
+  macro(Runtime1, is_instance_of) \
+  macro(Runtime1, predicate_failed_trap) \
+  macro(Runtime1, patch_code)
+
+#define INIT_COUNTER(sub, name) \
+  NEWPERFTICKCOUNTERS(_perf_##sub##_##name##_timer, SUN_CI, #sub "::" #name); \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_CI, #sub "::" #name "_count");
+
+void Runtime1::init_counters() {
+  assert(CompilerConfig::is_c1_enabled(), "");
+
+  if (UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS(INIT_COUNTER)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("Runtime1::init_counters() failed unexpectedly");
+    }
+  }
+}
+#undef INIT_COUNTER
+
+#define PRINT_COUNTER(sub, name) { \
+  if (_perf_##sub##_##name##_count != nullptr) {  \
+    jlong count = _perf_##sub##_##name##_count->get_value(); \
+    if (count > 0) { \
+      st->print_cr("  %-50s = " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) (" JLONG_FORMAT_W(5) " events)", #sub "::" #name, \
+                   _perf_##sub##_##name##_timer->elapsed_counter_value_us(), \
+                   _perf_##sub##_##name##_timer->thread_counter_value_us(), \
+                   count); \
+    }}}
+
+
+void Runtime1::print_counters_on(outputStream* st) {
+  if (UsePerfData && ProfileRuntimeCalls && CompilerConfig::is_c1_enabled()) {
+    DO_COUNTERS(PRINT_COUNTER)
+  } else {
+    st->print_cr("  Runtime1: no info (%s is disabled)",
+                 (!CompilerConfig::is_c1_enabled() ? "C1" : (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData")));
+  }
+}
+
+#undef PRINT_COUNTER
+#undef DO_COUNTERS
+
 #ifndef PRODUCT
-void Runtime1::print_statistics() {
-  tty->print_cr("C1 Runtime statistics:");
-  tty->print_cr(" _resolve_invoke_virtual_cnt:     %u", SharedRuntime::_resolve_virtual_ctr);
-  tty->print_cr(" _resolve_invoke_opt_virtual_cnt: %u", SharedRuntime::_resolve_opt_virtual_ctr);
-  tty->print_cr(" _resolve_invoke_static_cnt:      %u", SharedRuntime::_resolve_static_ctr);
-  tty->print_cr(" _handle_wrong_method_cnt:        %u", SharedRuntime::_wrong_method_ctr);
-  tty->print_cr(" _ic_miss_cnt:                    %u", SharedRuntime::_ic_miss_ctr);
-  tty->print_cr(" _generic_arraycopystub_cnt:      %u", _generic_arraycopystub_cnt);
-  tty->print_cr(" _byte_arraycopy_cnt:             %u", _byte_arraycopy_stub_cnt);
-  tty->print_cr(" _short_arraycopy_cnt:            %u", _short_arraycopy_stub_cnt);
-  tty->print_cr(" _int_arraycopy_cnt:              %u", _int_arraycopy_stub_cnt);
-  tty->print_cr(" _long_arraycopy_cnt:             %u", _long_arraycopy_stub_cnt);
-  tty->print_cr(" _oop_arraycopy_cnt:              %u", _oop_arraycopy_stub_cnt);
-  tty->print_cr(" _arraycopy_slowcase_cnt:         %u", _arraycopy_slowcase_cnt);
-  tty->print_cr(" _arraycopy_checkcast_cnt:        %u", _arraycopy_checkcast_cnt);
-  tty->print_cr(" _arraycopy_checkcast_attempt_cnt:%u", _arraycopy_checkcast_attempt_cnt);
-
-  tty->print_cr(" _new_type_array_slowcase_cnt:    %u", _new_type_array_slowcase_cnt);
-  tty->print_cr(" _new_object_array_slowcase_cnt:  %u", _new_object_array_slowcase_cnt);
-  tty->print_cr(" _new_instance_slowcase_cnt:      %u", _new_instance_slowcase_cnt);
-  tty->print_cr(" _new_multi_array_slowcase_cnt:   %u", _new_multi_array_slowcase_cnt);
-  tty->print_cr(" _monitorenter_slowcase_cnt:      %u", _monitorenter_slowcase_cnt);
-  tty->print_cr(" _monitorexit_slowcase_cnt:       %u", _monitorexit_slowcase_cnt);
-  tty->print_cr(" _patch_code_slowcase_cnt:        %u", _patch_code_slowcase_cnt);
-
-  tty->print_cr(" _throw_range_check_exception_count:            %u:", _throw_range_check_exception_count);
-  tty->print_cr(" _throw_index_exception_count:                  %u:", _throw_index_exception_count);
-  tty->print_cr(" _throw_div0_exception_count:                   %u:", _throw_div0_exception_count);
-  tty->print_cr(" _throw_null_pointer_exception_count:           %u:", _throw_null_pointer_exception_count);
-  tty->print_cr(" _throw_class_cast_exception_count:             %u:", _throw_class_cast_exception_count);
-  tty->print_cr(" _throw_incompatible_class_change_error_count:  %u:", _throw_incompatible_class_change_error_count);
-  tty->print_cr(" _throw_count:                                  %u:", _throw_count);
-
-  SharedRuntime::print_ic_miss_histogram();
-  tty->cr();
+void Runtime1::print_statistics_on(outputStream* st) {
+  st->print_cr("C1 Runtime statistics:");
+  st->print_cr(" _resolve_invoke_virtual_cnt:     %u", SharedRuntime::_resolve_virtual_ctr);
+  st->print_cr(" _resolve_invoke_opt_virtual_cnt: %u", SharedRuntime::_resolve_opt_virtual_ctr);
+  st->print_cr(" _resolve_invoke_static_cnt:      %u", SharedRuntime::_resolve_static_ctr);
+  st->print_cr(" _handle_wrong_method_cnt:        %u", SharedRuntime::_wrong_method_ctr);
+  st->print_cr(" _ic_miss_cnt:                    %u", SharedRuntime::_ic_miss_ctr);
+  st->print_cr(" _generic_arraycopystub_cnt:      %u", _generic_arraycopystub_cnt);
+  st->print_cr(" _byte_arraycopy_cnt:             %u", _byte_arraycopy_stub_cnt);
+  st->print_cr(" _short_arraycopy_cnt:            %u", _short_arraycopy_stub_cnt);
+  st->print_cr(" _int_arraycopy_cnt:              %u", _int_arraycopy_stub_cnt);
+  st->print_cr(" _long_arraycopy_cnt:             %u", _long_arraycopy_stub_cnt);
+  st->print_cr(" _oop_arraycopy_cnt:              %u", _oop_arraycopy_stub_cnt);
+  st->print_cr(" _arraycopy_slowcase_cnt:         %u", _arraycopy_slowcase_cnt);
+  st->print_cr(" _arraycopy_checkcast_cnt:        %u", _arraycopy_checkcast_cnt);
+  st->print_cr(" _arraycopy_checkcast_attempt_cnt:%u", _arraycopy_checkcast_attempt_cnt);
+
+  st->print_cr(" _new_type_array_slowcase_cnt:    %u", _new_type_array_slowcase_cnt);
+  st->print_cr(" _new_object_array_slowcase_cnt:  %u", _new_object_array_slowcase_cnt);
+  st->print_cr(" _new_instance_slowcase_cnt:      %u", _new_instance_slowcase_cnt);
+  st->print_cr(" _new_multi_array_slowcase_cnt:   %u", _new_multi_array_slowcase_cnt);
+  st->print_cr(" _monitorenter_slowcase_cnt:      %u", _monitorenter_slowcase_cnt);
+  st->print_cr(" _monitorexit_slowcase_cnt:       %u", _monitorexit_slowcase_cnt);
+  st->print_cr(" _patch_code_slowcase_cnt:        %u", _patch_code_slowcase_cnt);
+
+  st->print_cr(" _throw_range_check_exception_count:            %u:", _throw_range_check_exception_count);
+  st->print_cr(" _throw_index_exception_count:                  %u:", _throw_index_exception_count);
+  st->print_cr(" _throw_div0_exception_count:                   %u:", _throw_div0_exception_count);
+  st->print_cr(" _throw_null_pointer_exception_count:           %u:", _throw_null_pointer_exception_count);
+  st->print_cr(" _throw_class_cast_exception_count:             %u:", _throw_class_cast_exception_count);
+  st->print_cr(" _throw_incompatible_class_change_error_count:  %u:", _throw_incompatible_class_change_error_count);
+  st->print_cr(" _throw_count:                                  %u:", _throw_count);
+
+  SharedRuntime::print_ic_miss_histogram_on(st);
+  st->cr();
 }
 #endif // PRODUCT
diff a/src/hotspot/share/c1/c1_Runtime1.hpp b/src/hotspot/share/c1/c1_Runtime1.hpp
--- a/src/hotspot/share/c1/c1_Runtime1.hpp
+++ b/src/hotspot/share/c1/c1_Runtime1.hpp
@@ -160,9 +160,12 @@
 
   static void predicate_failed_trap(JavaThread* current);
 
   static void check_abort_on_vm_exception(oopDesc* ex);
 
-  static void print_statistics()                 PRODUCT_RETURN;
+  static void print_statistics_on(outputStream* st) PRODUCT_RETURN;
+
+  static void init_counters();
+  static void print_counters_on(outputStream* st);
 };
 
 #endif // SHARE_C1_C1_RUNTIME1_HPP
diff a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp
--- a/src/hotspot/share/cds/aotClassInitializer.cpp
+++ b/src/hotspot/share/cds/aotClassInitializer.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, 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.
@@ -24,15 +24,21 @@
 
 #include "precompiled.hpp"
 #include "cds/aotClassInitializer.hpp"
 #include "cds/archiveBuilder.hpp"
 #include "cds/cdsConfig.hpp"
+#include "dumpTimeClassInfo.inline.hpp"
 #include "cds/heapShared.hpp"
+#include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmSymbols.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/fieldStreams.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/symbol.hpp"
+#include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/javaCalls.hpp"
+#include "runtime/mutexLocker.hpp"
 
 // Detector for class names we wish to handle specially.
 // It is either an exact string match or a string prefix match.
 class AOTClassInitializer::AllowedSpec {
   const char* _class_name;
@@ -333,11 +339,13 @@
 // an annotation: @jdk.internal.vm.annotation.RuntimeSetupRequired
 // See JDK-8342481.
 bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) {
   return ik == vmClasses::Class_klass() ||
          ik == vmClasses::internal_Unsafe_klass() ||
-         ik == vmClasses::ConcurrentHashMap_klass();
+         ik == vmClasses::ConcurrentHashMap_klass() ||
+         ik->name()->equals("java/net/URI") ||
+         ik->name()->equals("java/lang/module/ModuleDescriptor");
 }
 
 void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) {
   assert(ik->has_aot_initialized_mirror(), "sanity");
   if (ik->is_runtime_setup_required()) {
@@ -355,5 +363,129 @@
       AOTLinkedClassBulkLoader::exit_on_exception(current);
     }
   }
 }
 
+// check_can_be_preinited() is quite costly, so we cache the results inside
+// DumpTimeClassInfo::_can_be_preinited. See also AOTClassInitializer::reset_preinit_check().
+bool AOTClassInitializer::check_can_be_preinited(InstanceKlass* ik) {
+  ResourceMark rm;
+
+  if (!SystemDictionaryShared::is_builtin(ik)) {
+    log_info(cds, init)("cannot initialize %s (not built-in loader)", ik->external_name());
+    return false;
+  }
+
+  InstanceKlass* super = ik->java_super();
+  if (super != nullptr && !can_be_preinited_locked(super)) {
+    log_info(cds, init)("cannot initialize %s (super %s not initable)", ik->external_name(), super->external_name());
+    return false;
+  }
+
+  Array<InstanceKlass*>* interfaces = ik->local_interfaces();
+  for (int i = 0; i < interfaces->length(); i++) {
+    if (!can_be_preinited_locked(interfaces->at(i))) {
+      log_info(cds, init)("cannot initialize %s (interface %s not initable)",
+                          ik->external_name(), interfaces->at(i)->external_name());
+      return false;
+    }
+  }
+
+  if (HeapShared::is_lambda_form_klass(ik)) {
+    // We allow only these to have <clinit> or non-default static fields
+    return true;
+  }
+
+  if (ik->class_initializer() != nullptr) {
+    log_info(cds, init)("cannot initialize %s (has <clinit>)", ik->external_name());
+    return false;
+  }
+  if (ik->is_initialized() && !has_default_static_fields(ik)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool AOTClassInitializer::has_default_static_fields(InstanceKlass* ik) {
+  oop mirror = ik->java_mirror();
+
+  for (JavaFieldStream fs(ik); !fs.done(); fs.next()) {
+    if (fs.access_flags().is_static()) {
+      fieldDescriptor& fd = fs.field_descriptor();
+      int offset = fd.offset();
+      bool is_default = true;
+      bool has_initval = fd.has_initial_value();
+      switch (fd.field_type()) {
+      case T_OBJECT:
+      case T_ARRAY:
+        is_default = mirror->obj_field(offset) == nullptr;
+        break;
+      case T_BOOLEAN:
+        is_default = mirror->bool_field(offset) == (has_initval ? fd.int_initial_value() : 0);
+        break;
+      case T_BYTE:
+        is_default = mirror->byte_field(offset) == (has_initval ? fd.int_initial_value() : 0);
+        break;
+      case T_SHORT:
+        is_default = mirror->short_field(offset) == (has_initval ? fd.int_initial_value() : 0);
+        break;
+      case T_CHAR:
+        is_default = mirror->char_field(offset) == (has_initval ? fd.int_initial_value() : 0);
+        break;
+      case T_INT:
+        is_default = mirror->int_field(offset) == (has_initval ? fd.int_initial_value() : 0);
+        break;
+      case T_LONG:
+        is_default = mirror->long_field(offset) == (has_initval ? fd.long_initial_value() : 0);
+        break;
+      case T_FLOAT:
+        is_default = mirror->float_field(offset) == (has_initval ? fd.float_initial_value() : 0);
+        break;
+      case T_DOUBLE:
+        is_default = mirror->double_field(offset) == (has_initval ? fd.double_initial_value() : 0);
+        break;
+      default:
+        ShouldNotReachHere();
+      }
+
+      if (!is_default) {
+        log_info(cds, init)("cannot initialize %s (static field %s has non-default value)",
+                            ik->external_name(), fd.name()->as_C_string());
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool AOTClassInitializer::can_be_preinited(InstanceKlass* ik) {
+  MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
+  return can_be_preinited_locked(ik);
+}
+
+bool AOTClassInitializer::can_be_preinited_locked(InstanceKlass* ik) {
+  if (!CDSConfig::is_initing_classes_at_dump_time()) {
+    return false;
+  }
+
+  assert_lock_strong(DumpTimeTable_lock);
+  DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(ik);
+  if (!info->has_done_preinit_check()) {
+    info->set_can_be_preinited(AOTClassInitializer::check_can_be_preinited(ik));
+  }
+  return info->can_be_preinited();
+}
+
+// Initialize a class at dump time, if possible.
+void AOTClassInitializer::maybe_preinit_class(InstanceKlass* ik, TRAPS) {
+#if 0 // FIXME -- leyden+JEP483 merge
+  if (!ik->is_initialized() && AOTClassInitializer::can_be_preinited(ik)) {
+    if (log_is_enabled(Info, cds, init)) {
+      ResourceMark rm;
+      log_info(cds, init)("preinitializing %s", ik->external_name());
+    }
+    ik->initialize(CHECK);
+  }
+#endif
+}
diff a/src/hotspot/share/cds/aotClassInitializer.hpp b/src/hotspot/share/cds/aotClassInitializer.hpp
--- a/src/hotspot/share/cds/aotClassInitializer.hpp
+++ b/src/hotspot/share/cds/aotClassInitializer.hpp
@@ -32,11 +32,22 @@
 
 class AOTClassInitializer : AllStatic {
   class AllowedSpec;
   static bool is_allowed(AllowedSpec* specs, InstanceKlass* ik);
 
+  static bool has_default_static_fields(InstanceKlass* ik);
+  static bool check_can_be_preinited(InstanceKlass* ik);
+  static bool can_be_preinited(InstanceKlass* ik);
+  static bool can_be_preinited_locked(InstanceKlass* ik);
+
 public:
+  // Called by metaspaceShared.cpp to speculatively initialize a class (if the results
+  // of initialization can be safely archived.
+  static void maybe_preinit_class(InstanceKlass* ik, TRAPS);
+
+  static void reset_preinit_check();
+
   // Called by heapShared.cpp to see if src_ik->java_mirror() can be archived in
   // the initialized state.
   static bool can_archive_initialized_mirror(InstanceKlass* src_ik);
 
   static bool is_runtime_setup_required(InstanceKlass* ik);
diff a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp
--- a/src/hotspot/share/cds/aotClassLinker.cpp
+++ b/src/hotspot/share/cds/aotClassLinker.cpp
@@ -139,10 +139,21 @@
 
   if (is_candidate(ik)) { // already checked.
     return true;
   }
 
+  if (!ik->is_linked() && SystemDictionaryShared::has_class_failed_verification(ik)) {
+    return false;
+  }
+
+/* should we allow old classes to be aot-linked ??? Probably ...
+  if (!k->can_be_verified_at_dumptime()) {
+    // linking old classes 
+    return false;
+  }
+*/
+
   if (ik->is_hidden()) {
     assert(ik->shared_class_loader_type() != ClassLoader::OTHER, "must have been set");
     if (!CDSConfig::is_dumping_invokedynamic()) {
       return false;
     }
diff a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
--- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp
+++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
@@ -24,21 +24,31 @@
 
 #include "precompiled.hpp"
 #include "cds/aotClassLinker.hpp"
 #include "cds/aotConstantPoolResolver.hpp"
 #include "cds/archiveBuilder.hpp"
+#include "cds/archiveUtils.inline.hpp"
 #include "cds/cdsConfig.hpp"
+#include "cds/classListWriter.hpp"
+#include "cds/finalImageRecipes.hpp"
+#include "cds/heapShared.hpp"
+#include "cds/lambdaFormInvokers.inline.hpp"
+#include "classfile/classLoader.hpp"
+#include "classfile/classLoaderExt.hpp"
+#include "classfile/dictionary.hpp"
+#include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "interpreter/bytecodeStream.hpp"
 #include "interpreter/interpreterRuntime.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/javaCalls.hpp"
 
 AOTConstantPoolResolver::ClassesTable* AOTConstantPoolResolver::_processed_classes = nullptr;
 
 void AOTConstantPoolResolver::initialize() {
   assert(_processed_classes == nullptr, "must be");
@@ -162,10 +172,33 @@
     case JVM_CONSTANT_String:
       resolve_string(cp, cp_index, CHECK); // may throw OOM when interning strings.
       break;
     }
   }
+
+  // Normally, we don't want to archive any CP entries that were not resolved
+  // in the training run. Otherwise the AOT/JIT may inline too much code that has not
+  // been executed.
+  //
+  // However, we want to aggressively resolve all klass/field/method constants for
+  // LambdaForm Invoker Holder classes, Lambda Proxy classes, and LambdaForm classes,
+  // so that the compiler can inline through them.
+  if (SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) {
+    bool eager_resolve = false;
+
+    if (LambdaFormInvokers::may_be_regenerated_class(ik->name())) {
+      eager_resolve = true;
+    }
+    if (ik->is_hidden() && HeapShared::is_archivable_hidden_klass(ik)) {
+      eager_resolve = true;
+    }
+
+    if (eager_resolve) {
+      preresolve_class_cp_entries(THREAD, ik, nullptr);
+      preresolve_field_and_method_cp_entries(THREAD, ik, nullptr);
+    }
+  }
 }
 
 // This works only for the boot/platform/app loaders
 Klass* AOTConstantPoolResolver::find_loaded_class(Thread* current, oop class_loader, Symbol* name) {
   HandleMark hm(current);
@@ -203,10 +236,18 @@
   }
 }
 #endif
 
 void AOTConstantPoolResolver::preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list) {
+  if (!CDSConfig::is_dumping_aot_linked_classes()) {
+    // TODO: Why is this check needed in Leyden?
+    // The following 3 tests fails when this "if" check is removed (when -XX:-AOTClassLinking is NOT enabled)
+    // - runtime/cds/appcds/methodHandles/MethodHandlesAsCollectorTest.java
+    // - runtime/cds/appcds/methodHandles/MethodHandlesGeneralTest.java
+    // - runtime/cds/appcds/methodHandles/MethodHandlesSpreadArgumentsTest.java
+    return;
+  }
   if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) {
     return;
   }
 
   JavaThread* THREAD = current;
@@ -244,10 +285,12 @@
     BytecodeStream bcs(methodHandle(THREAD, m));
     while (!bcs.is_last_bytecode()) {
       bcs.next();
       Bytecodes::Code raw_bc = bcs.raw_code();
       switch (raw_bc) {
+      case Bytecodes::_getstatic: // FIXME -- leyden+JEP483 merge
+      case Bytecodes::_putstatic: // FIXME -- leyden+JEP483 merge
       case Bytecodes::_getfield:
       case Bytecodes::_putfield:
         maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
         if (HAS_PENDING_EXCEPTION) {
           CLEAR_PENDING_EXCEPTION; // just ignore
@@ -255,10 +298,11 @@
         break;
       case Bytecodes::_invokehandle:
       case Bytecodes::_invokespecial:
       case Bytecodes::_invokevirtual:
       case Bytecodes::_invokeinterface:
+      case Bytecodes::_invokestatic: // FIXME -- leyden+JEP483 merge
         maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
         if (HAS_PENDING_EXCEPTION) {
           CLEAR_PENDING_EXCEPTION; // just ignore
         }
         break;
@@ -291,17 +335,38 @@
     // Do not resolve any field/methods from a class that has not been loaded yet.
     return;
   }
 
   Klass* resolved_klass = cp->klass_ref_at(raw_index, bc, CHECK);
+  const char* is_static = "";
 
   switch (bc) {
+#if 1 // FIXME -- leyden+JEP483 merge
+  case Bytecodes::_getstatic:
+  case Bytecodes::_putstatic:
+    if (!VM_Version::supports_fast_class_init_checks()) {
+      return; // Do not resolve since interpreter lacks fast clinit barriers support
+    }
+    InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK);
+    is_static = " *** static";
+    break;
+#endif
   case Bytecodes::_getfield:
   case Bytecodes::_putfield:
     InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK);
     break;
 
+#if 1 // FIXME -- leyden+JEP483 merge
+  case Bytecodes::_invokestatic:
+    if (!VM_Version::supports_fast_class_init_checks()) {
+      return; // Do not resolve since interpreter lacks fast clinit barriers support
+    }
+    InterpreterRuntime::cds_resolve_invoke(bc, raw_index, cp, CHECK);
+    is_static = " *** static";
+    break;
+#endif
+
   case Bytecodes::_invokevirtual:
   case Bytecodes::_invokespecial:
   case Bytecodes::_invokeinterface:
     InterpreterRuntime::cds_resolve_invoke(bc, raw_index, cp, CHECK);
     break;
@@ -317,15 +382,15 @@
   if (log_is_enabled(Trace, cds, resolve)) {
     ResourceMark rm(THREAD);
     bool resolved = cp->is_resolved(raw_index, bc);
     Symbol* name = cp->name_ref_at(raw_index, bc);
     Symbol* signature = cp->signature_ref_at(raw_index, bc);
-    log_trace(cds, resolve)("%s %s [%3d] %s -> %s.%s:%s",
+    log_trace(cds, resolve)("%s %s [%3d] %s -> %s.%s:%s%s",
                             (resolved ? "Resolved" : "Failed to resolve"),
                             Bytecodes::name(bc), cp_index, ik->external_name(),
                             resolved_klass->external_name(),
-                            name->as_C_string(), signature->as_C_string());
+                            name->as_C_string(), signature->as_C_string(), is_static);
   }
 }
 
 void AOTConstantPoolResolver::preresolve_indy_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list) {
   JavaThread* THREAD = current;
@@ -569,5 +634,155 @@
   } else {
     return ArchiveBuilder::current()->is_in_buffer_space(p);
   }
 }
 #endif
+
+int AOTConstantPoolResolver::class_reflection_data_flags(InstanceKlass* ik, TRAPS) {
+  assert(java_lang_Class::has_reflection_data(ik->java_mirror()), "must be");
+
+  HandleMark hm(THREAD);
+  JavaCallArguments args(Handle(THREAD, ik->java_mirror()));
+  JavaValue result(T_INT);
+  JavaCalls::call_special(&result,
+                          vmClasses::Class_klass(),
+                          vmSymbols::encodeReflectionData_name(),
+                          vmSymbols::void_int_signature(),
+                          &args, CHECK_0);
+  int flags = result.get_jint();
+  log_info(cds)("Encode ReflectionData: %s (flags=0x%x)", ik->external_name(), flags);
+  return flags;
+}
+
+void AOTConstantPoolResolver::generate_reflection_data(JavaThread* current, InstanceKlass* ik, int rd_flags) {
+  log_info(cds)("Generate ReflectionData: %s (flags=" INT32_FORMAT_X ")", ik->external_name(), rd_flags);
+  JavaThread* THREAD = current; // for exception macros
+  JavaCallArguments args(Handle(THREAD, ik->java_mirror()));
+  args.push_int(rd_flags);
+  JavaValue result(T_OBJECT);
+  JavaCalls::call_special(&result,
+                          vmClasses::Class_klass(),
+                          vmSymbols::generateReflectionData_name(),
+                          vmSymbols::int_void_signature(),
+                          &args, THREAD);
+  if (HAS_PENDING_EXCEPTION) {
+    Handle exc_handle(THREAD, PENDING_EXCEPTION);
+    CLEAR_PENDING_EXCEPTION;
+
+    log_warning(cds)("Exception during Class::generateReflectionData() call for %s", ik->external_name());
+    LogStreamHandle(Debug, cds) log;
+    if (log.is_enabled()) {
+      java_lang_Throwable::print_stack_trace(exc_handle, &log);
+    }
+  }
+}
+
+Klass* AOTConstantPoolResolver::resolve_boot_class_or_fail(const char* class_name, TRAPS) {
+  Handle class_loader;
+  Handle protection_domain;
+  TempNewSymbol class_name_sym = SymbolTable::new_symbol(class_name);
+  return SystemDictionary::resolve_or_fail(class_name_sym, class_loader, protection_domain, true, THREAD);
+}
+
+void AOTConstantPoolResolver::trace_dynamic_proxy_class(oop loader, const char* proxy_name, objArrayOop interfaces, int access_flags) {
+  if (interfaces->length() < 1) {
+    return;
+  }
+  if (ClassListWriter::is_enabled()) {
+    const char* loader_name = ArchiveUtils::builtin_loader_name_or_null(loader);
+    if (loader_name != nullptr) {
+      stringStream ss;
+      ss.print("%s %s %d %d", loader_name, proxy_name, access_flags, interfaces->length());
+      for (int i = 0; i < interfaces->length(); i++) {
+        oop mirror = interfaces->obj_at(i);
+        Klass* k = java_lang_Class::as_Klass(mirror);
+        ss.print(" %s", k->name()->as_C_string());
+      }
+      ClassListWriter w;
+      w.stream()->print_cr("@dynamic-proxy %s", ss.freeze());
+    }
+  }
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    FinalImageRecipes::add_dynamic_proxy_class(loader, proxy_name, interfaces, access_flags);
+  }
+}
+
+void AOTConstantPoolResolver::init_dynamic_proxy_cache(TRAPS) {
+  static bool inited = false;
+  if (inited) {
+    return;
+  }
+  inited = true;
+
+  Klass* klass = resolve_boot_class_or_fail("java/lang/reflect/Proxy", CHECK);
+  TempNewSymbol method = SymbolTable::new_symbol("initCacheForCDS");
+  TempNewSymbol signature = SymbolTable::new_symbol("(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)V");
+
+  JavaCallArguments args;
+  args.push_oop(Handle(THREAD, SystemDictionary::java_platform_loader()));
+  args.push_oop(Handle(THREAD, SystemDictionary::java_system_loader()));
+  JavaValue result(T_VOID);
+  JavaCalls::call_static(&result,
+                         klass,
+                         method,
+                         signature,
+                         &args, CHECK);
+}
+
+
+void AOTConstantPoolResolver::define_dynamic_proxy_class(Handle loader, Handle proxy_name, Handle interfaces, int access_flags, TRAPS) {
+  if (!CDSConfig::is_dumping_dynamic_proxies()) {
+    return;
+  }
+  init_dynamic_proxy_cache(CHECK);
+
+  Klass* klass = resolve_boot_class_or_fail("java/lang/reflect/Proxy$ProxyBuilder", CHECK);
+  TempNewSymbol method = SymbolTable::new_symbol("defineProxyClassForCDS");
+  TempNewSymbol signature = SymbolTable::new_symbol("(Ljava/lang/ClassLoader;Ljava/lang/String;[Ljava/lang/Class;I)Ljava/lang/Class;");
+
+  JavaCallArguments args;
+  args.push_oop(Handle(THREAD, loader()));
+  args.push_oop(Handle(THREAD, proxy_name()));
+  args.push_oop(Handle(THREAD, interfaces()));
+  args.push_int(access_flags);
+  JavaValue result(T_OBJECT);
+  JavaCalls::call_static(&result,
+                         klass,
+                         method,
+                         signature,
+                         &args, CHECK);
+
+  // Assumptions:
+  // FMG is archived, which means -modulepath and -Xbootclasspath are both not specified.
+  // All named modules are loaded from the system modules files.
+  // TODO: test support for -Xbootclasspath after JDK-8322322. Some of the code below need to be changed.
+  // TODO: we just give dummy shared_classpath_index for the generated class so that it will be archived.
+  //       The index is not used at runtime (see SystemDictionaryShared::load_shared_class_for_builtin_loader, which
+  //       uses a null ProtectionDomain for this class)
+  oop mirror = result.get_oop();
+  assert(mirror != nullptr, "class must have been generated if not OOM");
+  InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(mirror));
+  if (ik->is_shared_boot_class() || ik->is_shared_platform_class()) {
+    assert(ik->module()->is_named(), "dynamic proxies defined in unnamed modules for boot/platform loaders not supported");
+    ik->set_shared_classpath_index(0);
+  } else {
+    assert(ik->is_shared_app_class(), "must be");
+    ik->set_shared_classpath_index(ClassLoaderExt::app_class_paths_start_index());
+  }
+
+  ArchiveBuilder::alloc_stats()->record_dynamic_proxy_class();
+  if (log_is_enabled(Info, cds, dynamic, proxy)) {
+    ResourceMark rm(THREAD);
+    stringStream ss;
+    const char* prefix = "";
+    ss.print("%s (%-7s, cp index = %d) implements ", ik->external_name(),
+             ArchiveUtils::builtin_loader_name(loader()), ik->shared_classpath_index());
+    objArrayOop intfs = (objArrayOop)interfaces();
+    for (int i = 0; i < intfs->length(); i++) {
+      oop intf_mirror = intfs->obj_at(i);
+      ss.print("%s%s", prefix, java_lang_Class::as_Klass(intf_mirror)->external_name());
+      prefix = ", ";
+    }
+
+    log_info(cds, dynamic, proxy)("%s", ss.freeze());
+  }
+}
diff a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp
--- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp
+++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp
@@ -65,10 +65,16 @@
 
   static void resolve_string(constantPoolHandle cp, int cp_index, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
   static bool is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class);
   static bool is_indy_resolution_deterministic(ConstantPool* cp, int cp_index);
 
+  // helper
+  static Klass* resolve_boot_class_or_fail(const char* class_name, TRAPS);
+
+  // java/lang/reflect/Proxy caching
+  static void init_dynamic_proxy_cache(TRAPS);
+
   static Klass* find_loaded_class(Thread* current, oop class_loader, Symbol* name);
   static Klass* find_loaded_class(Thread* current, ConstantPool* cp, int class_cp_index);
 
   // fmi = FieldRef/MethodRef/InterfaceMethodRef
   static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index,
@@ -85,10 +91,17 @@
 
   static void preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
   static void preresolve_field_and_method_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
   static void preresolve_indy_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
 
+  // java/lang/Class$ReflectionData caching
+  static int class_reflection_data_flags(InstanceKlass* ik, TRAPS);
+  static void generate_reflection_data(JavaThread* current, InstanceKlass* ik, int rd_flags);
+
+  // java/lang/reflect/Proxy caching
+  static void trace_dynamic_proxy_class(oop loader, const char* proxy_name, objArrayOop interfaces, int access_flags);
+  static void define_dynamic_proxy_class(Handle loader, Handle proxy_name, Handle interfaces, int access_flags, TRAPS);
 
   // Resolve all constant pool entries that are safe to be stored in the
   // CDS archive.
   static void dumptime_resolve_constants(InstanceKlass* ik, TRAPS);
 
diff a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
--- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
+++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
@@ -25,30 +25,92 @@
 #include "precompiled.hpp"
 #include "cds/aotClassInitializer.hpp"
 #include "cds/aotClassLinker.hpp"
 #include "cds/aotLinkedClassBulkLoader.hpp"
 #include "cds/aotLinkedClassTable.hpp"
+#include "cds/archiveBuilder.hpp"
+#include "cds/archiveUtils.inline.hpp"
+#include "cds/cdsAccess.hpp"
 #include "cds/cdsConfig.hpp"
+#include "cds/cdsProtectionDomain.hpp"
 #include "cds/heapShared.hpp"
+#include "cds/lambdaFormInvokers.inline.hpp"
+#include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/classLoaderData.hpp"
+#include "classfile/classLoaderExt.hpp"
+#include "classfile/classLoader.hpp"
+#include "classfile/dictionary.hpp"
+#include "classfile/javaClasses.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
+#include "compiler/compilationPolicy.hpp"
 #include "gc/shared/gcVMOperations.hpp"
 #include "memory/resourceArea.hpp"
+#include "oops/constantPool.inline.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/trainingData.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/javaCalls.hpp"
 #include "runtime/java.hpp"
+#include "runtime/perfData.inline.hpp"
+#include "runtime/timer.hpp"
+#include "services/management.hpp"
 
 bool AOTLinkedClassBulkLoader::_boot2_completed = false;
 bool AOTLinkedClassBulkLoader::_platform_completed = false;
 bool AOTLinkedClassBulkLoader::_app_completed = false;
 bool AOTLinkedClassBulkLoader::_all_completed = false;
 
+Array<InstanceKlass*>* AOTLinkedClassBulkLoader::_unregistered_classes_from_preimage = nullptr;
+
+static PerfCounter* _perf_classes_preloaded = nullptr;
+static PerfTickCounters* _perf_class_preload_counters = nullptr;
+
+void AOTLinkedClassBulkLoader::record_unregistered_classes() {
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    GrowableArray<InstanceKlass*> unreg_classes;
+    GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses();
+    for (int i = 0; i < klasses->length(); i++) {
+      Klass* k = klasses->at(i);
+      if (k->is_instance_klass()) {
+        InstanceKlass* ik = InstanceKlass::cast(k);
+        if (ik->is_shared_unregistered_class()) {
+          unreg_classes.append((InstanceKlass*)ArchiveBuilder::get_buffered_klass(ik));
+        }
+      }
+    }
+    _unregistered_classes_from_preimage = ArchiveUtils::archive_array(&unreg_classes);
+  } else {
+    _unregistered_classes_from_preimage = nullptr;
+  }
+}
+
 void AOTLinkedClassBulkLoader::serialize(SerializeClosure* soc, bool is_static_archive) {
   AOTLinkedClassTable::get(is_static_archive)->serialize(soc);
+
+  if (is_static_archive) {
+    soc->do_ptr((void**)&_unregistered_classes_from_preimage);
+
+    if (soc->reading() && UsePerfData) {
+      JavaThread* THREAD = JavaThread::current();
+      NEWPERFEVENTCOUNTER(_perf_classes_preloaded, SUN_CLS, "preloadedClasses");
+      NEWPERFTICKCOUNTERS(_perf_class_preload_counters, SUN_CLS, "classPreload");
+    }
+  }
+}
+
+bool AOTLinkedClassBulkLoader::class_preloading_finished() {
+  if (!CDSConfig::is_using_aot_linked_classes()) {
+    return true;
+  } else {
+    // The ConstantPools of preloaded classes have references to other preloaded classes. We don't
+    // want any Java code (including JVMCI compiler) to use these classes until all of them
+    // are loaded.
+    return Atomic::load_acquire(&_all_completed);
+  }
 }
 
 void AOTLinkedClassBulkLoader::load_javabase_classes(JavaThread* current) {
   assert(CDSConfig::is_using_aot_linked_classes(), "sanity");
   load_classes_in_loader(current, AOTLinkedClassCategory::BOOT1, nullptr); // only java.base classes
@@ -58,23 +120,44 @@
   assert(CDSConfig::is_using_aot_linked_classes(), "sanity");
 
   // is_using_aot_linked_classes() requires is_using_full_module_graph(). As a result,
   // the platform/system class loader should already have been initialized as part
   // of the FMG support.
-  assert(CDSConfig::is_using_full_module_graph(), "must be");
+  if (!CDSConfig::is_dumping_final_static_archive()) {
+    assert(CDSConfig::is_using_full_module_graph(), "must be");
+  }
   assert(SystemDictionary::java_platform_loader() != nullptr, "must be");
   assert(SystemDictionary::java_system_loader() != nullptr,   "must be");
 
   load_classes_in_loader(current, AOTLinkedClassCategory::BOOT2, nullptr); // all boot classes outside of java.base
   _boot2_completed = true;
 
   load_classes_in_loader(current, AOTLinkedClassCategory::PLATFORM, SystemDictionary::java_platform_loader());
   _platform_completed = true;
 
   load_classes_in_loader(current, AOTLinkedClassCategory::APP, SystemDictionary::java_system_loader());
+
+  if (PrintTrainingInfo) {
+    tty->print_cr("==================== archived_training_data ** after all classes preloaded ====================");
+    TrainingData::print_archived_training_data_on(tty);
+  }
+
+  if (log_is_enabled(Info, cds, jit)) {
+    CDSAccess::test_heap_access_api();
+  }
+
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    assert(_unregistered_classes_from_preimage != nullptr, "must be");
+    for (int i = 0; i < _unregistered_classes_from_preimage->length(); i++) {
+      InstanceKlass* ik = _unregistered_classes_from_preimage->at(i);
+      SystemDictionaryShared::init_dumptime_info(ik);
+      SystemDictionaryShared::add_unregistered_class(current, ik);
+    }
+  }
+
   _app_completed = true;
-  _all_completed = true;
+  Atomic::release_store(&_all_completed, true);
 }
 
 void AOTLinkedClassBulkLoader::load_classes_in_loader(JavaThread* current, AOTLinkedClassCategory class_category, oop class_loader_oop) {
   load_classes_in_loader_impl(class_category, class_loader_oop, current);
   if (current->has_pending_exception()) {
@@ -177,10 +260,13 @@
   }
 
   ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(loader());
 
   for (int i = 0; i < classes->length(); i++) {
+    if (UsePerfData) {
+      _perf_classes_preloaded->inc();
+    }
     InstanceKlass* ik = classes->at(i);
     if (log_is_enabled(Info, cds, aot, load)) {
       ResourceMark rm(THREAD);
       log_info(cds, aot, load)("%-5s %s%s%s", category_name, ik->external_name(),
                                ik->is_loaded() ? " (already loaded)" : "",
@@ -393,5 +479,38 @@
     return !_app_completed;
   } else {
     return false;
   }
 }
+
+void AOTLinkedClassBulkLoader::replay_training_at_init(Array<InstanceKlass*>* classes, TRAPS) {
+  if (classes != nullptr) {
+    for (int i = 0; i < classes->length(); i++) {
+      InstanceKlass* ik = classes->at(i);
+      if (ik->has_aot_initialized_mirror() && ik->is_initialized() && !ik->has_init_deps_processed()) {
+        CompilationPolicy::replay_training_at_init(ik, CHECK);
+      }
+    }
+  }
+}
+
+void AOTLinkedClassBulkLoader::replay_training_at_init_for_preloaded_classes(TRAPS) {
+  if (CDSConfig::is_using_aot_linked_classes() && TrainingData::have_data()) {
+    AOTLinkedClassTable* table = AOTLinkedClassTable::for_static_archive(); // not applicable for dynamic archive (?? why??)
+    replay_training_at_init(table->boot(),     CHECK);
+    replay_training_at_init(table->boot2(),    CHECK);
+    replay_training_at_init(table->platform(), CHECK);
+    replay_training_at_init(table->app(),      CHECK);
+
+    CompilationPolicy::replay_training_at_init(false, CHECK);
+  }
+}
+
+void AOTLinkedClassBulkLoader::print_counters_on(outputStream* st) {
+  if (UsePerfData && _perf_class_preload_counters != nullptr) {
+    st->print_cr("AOTLinkedClassBulkLoader:");
+    st->print_cr("  preload:           " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) " (thread) / " JLONG_FORMAT_W(5) " events",
+                 _perf_class_preload_counters->elapsed_counter_value_us(),
+                 _perf_class_preload_counters->thread_counter_value_us(),
+                 _perf_classes_preloaded->get_value());
+  }
+}
diff a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp
--- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp
+++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp
@@ -45,25 +45,31 @@
 class AOTLinkedClassBulkLoader :  AllStatic {
   static bool _boot2_completed;
   static bool _platform_completed;
   static bool _app_completed;
   static bool _all_completed;
+  static bool _preloading_non_javavase_classes;
+  static Array<InstanceKlass*>* _unregistered_classes_from_preimage;
+
   static void load_classes_in_loader(JavaThread* current, AOTLinkedClassCategory class_category, oop class_loader_oop);
   static void load_classes_in_loader_impl(AOTLinkedClassCategory class_category, oop class_loader_oop, TRAPS);
   static void load_table(AOTLinkedClassTable* table, AOTLinkedClassCategory class_category, Handle loader, TRAPS);
   static void initiate_loading(JavaThread* current, const char* category, Handle initiating_loader, Array<InstanceKlass*>* classes);
   static void load_classes_impl(AOTLinkedClassCategory class_category, Array<InstanceKlass*>* classes,
                                 const char* category_name, Handle loader, TRAPS);
   static void load_hidden_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS);
   static void init_required_classes_for_loader(Handle class_loader, Array<InstanceKlass*>* classes, TRAPS);
+  static void replay_training_at_init(Array<InstanceKlass*>* classes, TRAPS) NOT_CDS_RETURN;
 public:
   static void serialize(SerializeClosure* soc, bool is_static_archive) NOT_CDS_RETURN;
-
+  static void record_unregistered_classes() NOT_CDS_RETURN;
   static void load_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
   static void load_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
   static void finish_loading_javabase_classes(TRAPS) NOT_CDS_RETURN;
   static void exit_on_exception(JavaThread* current);
-
+  static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN;
+  static bool class_preloading_finished();
+  static void print_counters_on(outputStream* st) NOT_CDS_RETURN;
   static bool is_pending_aot_linked_class(Klass* k) NOT_CDS_RETURN_(false);
 };
 
 #endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP
diff a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp
--- a/src/hotspot/share/cds/archiveBuilder.cpp
+++ b/src/hotspot/share/cds/archiveBuilder.cpp
@@ -30,10 +30,11 @@
 #include "cds/archiveUtils.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/cppVtables.hpp"
 #include "cds/dumpAllocStats.hpp"
 #include "cds/dynamicArchive.hpp"
+#include "cds/finalImageRecipes.hpp"
 #include "cds/heapShared.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "cds/regeneratedClasses.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderDataShared.hpp"
@@ -49,13 +50,16 @@
 #include "memory/allStatic.hpp"
 #include "memory/memRegion.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/compressedKlass.inline.hpp"
 #include "oops/instanceKlass.hpp"
+#include "oops/methodCounters.hpp"
+#include "oops/methodData.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oopHandle.inline.hpp"
+#include "oops/trainingData.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/globals_extension.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/sharedRuntime.hpp"
@@ -162,13 +166,15 @@
   _mapped_static_archive_bottom(nullptr),
   _mapped_static_archive_top(nullptr),
   _buffer_to_requested_delta(0),
   _rw_region("rw", MAX_SHARED_DELTA),
   _ro_region("ro", MAX_SHARED_DELTA),
+  _cc_region("cc", MAX_SHARED_DELTA),
   _ptrmap(mtClassShared),
   _rw_ptrmap(mtClassShared),
   _ro_ptrmap(mtClassShared),
+  _cc_ptrmap(mtClassShared),
   _rw_src_objs(),
   _ro_src_objs(),
   _src_obj_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE),
   _buffered_to_src_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE),
   _total_heap_region_size(0),
@@ -316,11 +322,21 @@
 
 size_t ArchiveBuilder::estimate_archive_size() {
   // size of the symbol table and two dictionaries, plus the RunTimeClassInfo's
   size_t symbol_table_est = SymbolTable::estimate_size_for_archive();
   size_t dictionary_est = SystemDictionaryShared::estimate_size_for_archive();
-  _estimated_hashtable_bytes = symbol_table_est + dictionary_est;
+  size_t training_data_est = TrainingData::estimate_size_for_archive();
+  _estimated_hashtable_bytes = symbol_table_est + dictionary_est + training_data_est;
+
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    _estimated_hashtable_bytes += 200 * 1024 * 1024; // FIXME -- need to iterate archived symbols??
+  }
+
+  if (CDSConfig::is_dumping_dynamic_archive()) {
+    // Some extra space for traning data. Be generous. Unused areas will be trimmed from the archive file.
+    _estimated_hashtable_bytes += 200 * 1024 * 1024;
+  }
 
   if (CDSConfig::is_dumping_aot_linked_classes()) {
     // This is difficult to estimate when dumping the dynamic archive, as the
     // AOTLinkedClassTable may need to contain classes in the static archive as well.
     //
@@ -545,11 +561,11 @@
   if (klass->is_instance_klass()) {
     InstanceKlass* ik = InstanceKlass::cast(klass);
     return SystemDictionaryShared::is_excluded_class(ik);
   } else if (klass->is_objArray_klass()) {
     Klass* bottom = ObjArrayKlass::cast(klass)->bottom_klass();
-    if (MetaspaceShared::is_shared_static(bottom)) {
+    if (CDSConfig::is_dumping_dynamic_archive() && MetaspaceShared::is_shared_static(bottom)) {
       // The bottom class is in the static archive so it's clearly not excluded.
       assert(CDSConfig::is_dumping_dynamic_archive(), "sanity");
       return false;
     } else if (bottom->is_instance_klass()) {
       return SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(bottom));
@@ -559,16 +575,19 @@
   return false;
 }
 
 ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref *ref) {
   address obj = ref->obj();
-  if (MetaspaceShared::is_in_shared_metaspace(obj)) {
+  if (CDSConfig::is_dumping_dynamic_archive() && MetaspaceShared::is_in_shared_metaspace(obj)) {
     // Don't dump existing shared metadata again.
     return point_to_it;
   } else if (ref->msotype() == MetaspaceObj::MethodDataType ||
-             ref->msotype() == MetaspaceObj::MethodCountersType) {
-    return set_to_null;
+             ref->msotype() == MetaspaceObj::MethodCountersType ||
+             ref->msotype() == MetaspaceObj::KlassTrainingDataType ||
+             ref->msotype() == MetaspaceObj::MethodTrainingDataType ||
+             ref->msotype() == MetaspaceObj::CompileTrainingDataType) {
+      return TrainingData::need_data() ? make_a_copy : set_to_null;
   } else {
     if (ref->msotype() == MetaspaceObj::ClassType) {
       Klass* klass = (Klass*)ref->obj();
       assert(klass->is_klass(), "must be");
       if (is_excluded(klass)) {
@@ -776,10 +795,15 @@
          p2i(src_addr));
 
   return p->buffered_addr();
 }
 
+bool ArchiveBuilder::has_been_archived(address src_addr) const {
+  SourceObjInfo* p = _src_obj_table.get(src_addr);
+  return (p != nullptr);
+}
+
 address ArchiveBuilder::get_source_addr(address buffered_addr) const {
   assert(is_in_buffer_space(buffered_addr), "must be");
   address* src_p = _buffered_to_src_table.get(buffered_addr);
   assert(src_p != nullptr && *src_p != nullptr, "must be");
   return *src_p;
@@ -989,15 +1013,39 @@
 #undef STATS_PARAMS
 
   DynamicArchive::make_array_klasses_shareable();
 }
 
+void ArchiveBuilder::make_training_data_shareable() {
+  auto clean_td = [&] (address& src_obj,  SourceObjInfo& info) {
+    if (!is_in_buffer_space(info.buffered_addr())) {
+      return;
+    }
+
+    if (info.msotype() == MetaspaceObj::KlassTrainingDataType ||
+        info.msotype() == MetaspaceObj::MethodTrainingDataType ||
+        info.msotype() == MetaspaceObj::CompileTrainingDataType) {
+      TrainingData* buffered_td = (TrainingData*)info.buffered_addr();
+      buffered_td->remove_unshareable_info();
+    } else if (info.msotype() == MetaspaceObj::MethodDataType) {
+      MethodData* buffered_mdo = (MethodData*)info.buffered_addr();
+      buffered_mdo->remove_unshareable_info();
+    } else if (info.msotype() == MetaspaceObj::MethodCountersType) {
+      MethodCounters* buffered_mc = (MethodCounters*)info.buffered_addr();
+      buffered_mc->remove_unshareable_info();
+    }
+  };
+  _src_obj_table.iterate_all(clean_td);
+}
+
 void ArchiveBuilder::serialize_dynamic_archivable_items(SerializeClosure* soc) {
   SymbolTable::serialize_shared_table_header(soc, false);
   SystemDictionaryShared::serialize_dictionary_headers(soc, false);
   DynamicArchive::serialize_array_klasses(soc);
   AOTLinkedClassBulkLoader::serialize(soc, false);
+  FinalImageRecipes::serialize(soc, false);
+  TrainingData::serialize_training_data(soc);
 }
 
 uintx ArchiveBuilder::buffer_to_offset(address p) const {
   address requested_p = to_requested(p);
   assert(requested_p >= _requested_static_archive_bottom, "must be");
@@ -1014,10 +1062,19 @@
     p = get_buffered_addr(p);
   }
   return buffer_to_offset(p);
 }
 
+void ArchiveBuilder::start_cc_region() {
+  ro_region()->pack();
+  start_dump_region(&_cc_region);
+}
+
+void ArchiveBuilder::end_cc_region() {
+  _cc_region.pack();
+}
+
 address ArchiveBuilder::offset_to_buffered_address(u4 offset) const {
   address requested_addr = _requested_static_archive_bottom + offset;
   address buffered_addr = requested_addr - _buffer_to_requested_delta;
   assert(is_in_buffer_space(buffered_addr), "bad offset");
   return buffered_addr;
@@ -1130,11 +1187,13 @@
   return UseCompactObjectHeaders ?  CompressedKlassPointers::max_shift() : 0;
 }
 #endif // _LP64
 
 void ArchiveBuilder::relocate_to_requested() {
-  ro_region()->pack();
+  if (!ro_region()->is_packed()) {
+    ro_region()->pack();
+  }
 
   size_t my_archive_size = buffer_top() - buffer_bottom();
 
   if (CDSConfig::is_dumping_static_archive()) {
     _requested_static_archive_top = _requested_static_archive_bottom + my_archive_size;
@@ -1392,20 +1451,30 @@
         }
       } else {
         st.print_cr(" - fields (" SIZE_FORMAT " words):", source_oop->size());
         ArchivedFieldPrinter print_field(heap_info, &st, source_oop, buffered_addr);
         InstanceKlass::cast(source_klass)->print_nonstatic_fields(&print_field);
+
+        if (java_lang_Class::is_instance(source_oop)) {
+          st.print(" - signature: ");
+          if (java_lang_Class::is_primitive(source_oop)) {
+            st.print("primitive ??");
+          } else {
+            java_lang_Class::print_signature(source_oop, &st);
+          }
+          st.cr();
+        }
       }
     }
   }
 
   static void log_heap_roots() {
     LogStreamHandle(Trace, cds, map, oops) st;
     if (st.is_enabled()) {
       for (int i = 0; i < HeapShared::pending_roots()->length(); i++) {
         st.print("roots[%4d]: ", i);
-        print_oop_with_requested_addr_cr(&st, HeapShared::pending_roots()->at(i));
+        print_oop_with_requested_addr_cr(&st, HeapShared::pending_roots()->at(i).resolve());
       }
     }
   }
 
   // The output looks like this. The first number is the requested address. The second number is
@@ -1426,11 +1495,16 @@
       }
       if (source_oop->is_array()) {
         int array_len = arrayOop(source_oop)->length();
         st->print_cr("%s length: %d", source_oop->klass()->external_name(), array_len);
       } else {
-        st->print_cr("%s", source_oop->klass()->external_name());
+        st->print("%s", source_oop->klass()->external_name());
+        if (java_lang_invoke_MethodType::is_instance(source_oop)) {
+          st->print(" ");
+          java_lang_invoke_MethodType::print_signature(source_oop, st);
+        }
+        st->cr();
       }
     }
   }
 #endif // INCLUDE_CDS_JAVA_HEAP
 
@@ -1499,16 +1573,20 @@
   // MetaspaceShared::n_regions (internal to hotspot).
   assert(NUM_CDS_REGIONS == MetaspaceShared::n_regions, "sanity");
 
   write_region(mapinfo, MetaspaceShared::rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false);
   write_region(mapinfo, MetaspaceShared::ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false);
+  write_region(mapinfo, MetaspaceShared::cc, &_cc_region, /*read_only=*/false,/*allow_exec=*/false);
 
   // Split pointer map into read-write and read-only bitmaps
-  ArchivePtrMarker::initialize_rw_ro_maps(&_rw_ptrmap, &_ro_ptrmap);
+  ArchivePtrMarker::initialize_rw_ro_cc_maps(&_rw_ptrmap, &_ro_ptrmap, &_cc_ptrmap);
 
   size_t bitmap_size_in_bytes;
-  char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::rw_ptrmap(), ArchivePtrMarker::ro_ptrmap(), heap_info,
+  char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::rw_ptrmap(),
+                                              ArchivePtrMarker::ro_ptrmap(),
+                                              ArchivePtrMarker::cc_ptrmap(),
+                                              heap_info,
                                               bitmap_size_in_bytes);
 
   if (heap_info->is_used()) {
     _total_heap_region_size = mapinfo->write_heap_region(heap_info);
   }
@@ -1550,10 +1628,11 @@
                              _total_heap_region_size;
   const double total_u_perc = percent_of(total_bytes, total_reserved);
 
   _rw_region.print(total_reserved);
   _ro_region.print(total_reserved);
+  _cc_region.print(total_reserved);
 
   print_bitmap_region_stats(bitmap_used, total_reserved);
 
   if (heap_info->is_used()) {
     print_heap_region_stats(heap_info, total_reserved);
diff a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp
--- a/src/hotspot/share/cds/archiveBuilder.hpp
+++ b/src/hotspot/share/cds/archiveBuilder.hpp
@@ -144,10 +144,14 @@
         _buffered_addr = ref->obj();
       } else {
         _buffered_addr = nullptr;
       }
     }
+    SourceObjInfo(address src, address buf) {
+      _source_addr = src;
+      _buffered_addr = buf;
+    }
 
     // This constructor is only used for regenerated objects (created by LambdaFormInvokers, etc).
     //   src = address of a Method or InstanceKlass that has been regenerated.
     //   renegerated_obj_info = info for the regenerated version of src.
     SourceObjInfo(address src, SourceObjInfo* renegerated_obj_info) :
@@ -210,18 +214,20 @@
   ReservedSpace _shared_rs;
   VirtualSpace _shared_vs;
 
   DumpRegion _rw_region;
   DumpRegion _ro_region;
+  DumpRegion _cc_region;
 
   // Combined bitmap to track pointers in both RW and RO regions. This is updated
   // as objects are copied into RW and RO.
   CHeapBitMap _ptrmap;
 
   // _ptrmap is split into these two bitmaps which are written into the archive.
   CHeapBitMap _rw_ptrmap;   // marks pointers in the RW region
   CHeapBitMap _ro_ptrmap;   // marks pointers in the RO region
+  CHeapBitMap _cc_ptrmap;   // marks pointers in the CC region
 
   SourceObjList _rw_src_objs;                 // objs to put in rw region
   SourceObjList _ro_src_objs;                 // objs to put in ro region
   ResizeableResourceHashtable<address, SourceObjInfo, AnyObj::C_HEAP, mtClassShared> _src_obj_table;
   ResizeableResourceHashtable<address, address, AnyObj::C_HEAP, mtClassShared> _buffered_to_src_table;
@@ -257,10 +263,11 @@
 
   void iterate_sorted_roots(MetaspaceClosure* it);
   void sort_klasses();
   static int compare_symbols_by_address(Symbol** a, Symbol** b);
   static int compare_klass_by_name(Klass** a, Klass** b);
+  void update_hidden_class_loader_type(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN;
 
   void make_shallow_copies(DumpRegion *dump_region, const SourceObjList* src_objs);
   void make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info);
 
   void relocate_embedded_pointers(SourceObjList* src_objs);
@@ -364,17 +371,24 @@
   void remember_embedded_pointer_in_enclosing_obj(MetaspaceClosure::Ref* ref);
   static void serialize_dynamic_archivable_items(SerializeClosure* soc);
 
   DumpRegion* rw_region() { return &_rw_region; }
   DumpRegion* ro_region() { return &_ro_region; }
+  DumpRegion* cc_region() { return &_cc_region; }
+
+  void start_cc_region();
+  void end_cc_region();
 
   static char* rw_region_alloc(size_t num_bytes) {
     return current()->rw_region()->allocate(num_bytes);
   }
   static char* ro_region_alloc(size_t num_bytes) {
     return current()->ro_region()->allocate(num_bytes);
   }
+  static char* cc_region_alloc(size_t num_bytes) {
+    return current()->cc_region()->allocate(num_bytes);
+  }
 
   template <typename T>
   static Array<T>* new_ro_array(int length) {
     size_t byte_size = Array<T>::byte_sizeof(length, sizeof(T));
     Array<T>* array = (Array<T>*)ro_region_alloc(byte_size);
@@ -403,10 +417,11 @@
   void dump_rw_metadata();
   void dump_ro_metadata();
   void relocate_metaspaceobj_embedded_pointers();
   void record_regenerated_object(address orig_src_obj, address regen_src_obj);
   void make_klasses_shareable();
+  void make_training_data_shareable();
   void relocate_to_requested();
   void write_archive(FileMapInfo* mapinfo, ArchiveHeapInfo* heap_info);
   void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region,
                     bool read_only,  bool allow_exec);
 
@@ -418,18 +433,20 @@
   void mark_and_relocate_to_buffered_addr(address* ptr_location);
   template <typename T> void mark_and_relocate_to_buffered_addr(T ptr_location) {
     mark_and_relocate_to_buffered_addr((address*)ptr_location);
   }
 
+  bool has_been_archived(address src_addr) const;
+
   bool has_been_buffered(address src_addr) const;
   template <typename T> bool has_been_buffered(T src_addr) const {
     return has_been_buffered((address)src_addr);
   }
-
   address get_buffered_addr(address src_addr) const;
   template <typename T> T get_buffered_addr(T src_addr) const {
-    return (T)get_buffered_addr((address)src_addr);
+    CDS_ONLY(return (T)get_buffered_addr((address)src_addr);)
+    NOT_CDS(return nullptr;)
   }
 
   address get_source_addr(address buffered_addr) const;
   template <typename T> T get_source_addr(T buffered_addr) const {
     return (T)get_source_addr((address)buffered_addr);
@@ -438,11 +455,12 @@
   // All klasses and symbols that will be copied into the archive
   GrowableArray<Klass*>*  klasses() const { return _klasses; }
   GrowableArray<Symbol*>* symbols() const { return _symbols; }
 
   static bool is_active() {
-    return (_current != nullptr);
+    CDS_ONLY(return (_current != nullptr));
+    NOT_CDS(return false;)
   }
 
   static ArchiveBuilder* current() {
     assert(_current != nullptr, "ArchiveBuilder must be active");
     return _current;
diff a/src/hotspot/share/cds/archiveHeapLoader.cpp b/src/hotspot/share/cds/archiveHeapLoader.cpp
--- a/src/hotspot/share/cds/archiveHeapLoader.cpp
+++ b/src/hotspot/share/cds/archiveHeapLoader.cpp
@@ -27,10 +27,11 @@
 #include "cds/cdsConfig.hpp"
 #include "cds/heapShared.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "classfile/classLoaderDataShared.hpp"
 #include "classfile/systemDictionaryShared.hpp"
+#include "classfile/vmClasses.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "logging/log.hpp"
 #include "memory/iterator.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
@@ -438,10 +439,25 @@
     HeapWord* top = (HeapWord*)_loaded_heap_top;
     Universe::heap()->fill_with_objects(bottom, top - bottom);
   }
 }
 
+oop ArchiveHeapLoader::oop_from_offset(int offset) {
+  // Once GC starts, the offsets saved in CachedCodeDirectoryInternal::_permanent_oop_offsets
+  // will become invalid. I don't know what function can check if GCs are allowed, but surely
+  // GCs can't happen before the Object class is loaded.
+  assert(CDSConfig::is_using_archive(), "sanity");
+  assert(vmClasses::Object_klass()->class_loader_data() == nullptr,
+         "can be called only very early during VM start-up");
+  if (is_loaded()) {
+    return cast_to_oop(_loaded_heap_bottom + offset);
+  } else {
+    assert(is_mapped(), "must be");
+    return cast_to_oop(_mapped_heap_bottom + offset);
+  }
+}
+
 class PatchNativePointers: public BitMapClosure {
   Metadata** _start;
 
  public:
   PatchNativePointers(Metadata** start) : _start(start) {}
diff a/src/hotspot/share/cds/archiveHeapLoader.hpp b/src/hotspot/share/cds/archiveHeapLoader.hpp
--- a/src/hotspot/share/cds/archiveHeapLoader.hpp
+++ b/src/hotspot/share/cds/archiveHeapLoader.hpp
@@ -152,10 +152,11 @@
 
   static bool load_heap_region(FileMapInfo* mapinfo);
   static void assert_in_loaded_heap(uintptr_t o) {
     assert(is_in_loaded_heap(o), "must be");
   }
+  static oop oop_from_offset(int offset);
 #endif // INCLUDE_CDS_JAVA_HEAP
 
 };
 
 #endif // SHARE_CDS_ARCHIVEHEAPLOADER_HPP
diff a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp
--- a/src/hotspot/share/cds/archiveHeapWriter.cpp
+++ b/src/hotspot/share/cds/archiveHeapWriter.cpp
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "cds/archiveHeapWriter.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/filemap.hpp"
 #include "cds/heapShared.hpp"
+#include "cds/regeneratedClasses.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "memory/iterator.inline.hpp"
 #include "memory/oopFactory.hpp"
@@ -58,10 +59,15 @@
 HeapRootSegments ArchiveHeapWriter::_heap_root_segments;
 
 address ArchiveHeapWriter::_requested_bottom;
 address ArchiveHeapWriter::_requested_top;
 
+static size_t _num_strings = 0;
+static size_t _string_bytes = 0; 
+static size_t _num_packages = 0;
+static size_t _num_protection_domains = 0;
+
 GrowableArrayCHeap<ArchiveHeapWriter::NativePointerInfo, mtClassShared>* ArchiveHeapWriter::_native_pointers;
 GrowableArrayCHeap<oop, mtClassShared>* ArchiveHeapWriter::_source_objs;
 GrowableArrayCHeap<ArchiveHeapWriter::HeapObjOrder, mtClassShared>* ArchiveHeapWriter::_source_objs_order;
 
 ArchiveHeapWriter::BufferOffsetToSourceObjectTable*
@@ -318,17 +324,22 @@
     oop src_obj = _source_objs->at(src_obj_index);
     HeapShared::CachedOopInfo* info = HeapShared::archived_object_cache()->get(src_obj);
     assert(info != nullptr, "must be");
     size_t buffer_offset = copy_one_source_obj_to_buffer(src_obj);
     info->set_buffer_offset(buffer_offset);
+    assert(buffer_offset <= 0x7fffffff, "sanity");
+    HeapShared::add_to_permanent_oop_table(src_obj, (int)buffer_offset);
 
     _buffer_offset_to_source_obj_table->put_when_absent(buffer_offset, src_obj);
     _buffer_offset_to_source_obj_table->maybe_grow();
   }
 
   log_info(cds)("Size of heap region = " SIZE_FORMAT " bytes, %d objects, %d roots, %d native ptrs",
                 _buffer_used, _source_objs->length() + 1, roots->length(), _num_native_ptrs);
+  log_info(cds)("   strings            = " SIZE_FORMAT_W(8) " (" SIZE_FORMAT " bytes)", _num_strings, _string_bytes);
+  log_info(cds)("   packages           = " SIZE_FORMAT_W(8), _num_packages);
+  log_info(cds)("   protection domains = " SIZE_FORMAT_W(8),_num_protection_domains);
 }
 
 size_t ArchiveHeapWriter::filler_array_byte_size(int length) {
   size_t byte_size = objArrayOopDesc::object_size(length) * HeapWordSize;
   return byte_size;
@@ -413,11 +424,29 @@
 void update_buffered_object_field(address buffered_obj, int field_offset, T value) {
   T* field_addr = cast_to_oop(buffered_obj)->field_addr<T>(field_offset);
   *field_addr = value;
 }
 
+void ArchiveHeapWriter::update_stats(oop src_obj) {
+  if (java_lang_String::is_instance(src_obj)) {
+    _num_strings ++;
+    _string_bytes += src_obj->size() * HeapWordSize;
+    _string_bytes += java_lang_String::value(src_obj)->size() * HeapWordSize;
+  } else {
+    Klass* k = src_obj->klass();
+    Symbol* name = k->name();
+    if (name->equals("java/lang/NamedPackage") || name->equals("java/lang/Package")) {
+      _num_packages ++;
+    } else if (name->equals("java/security/ProtectionDomain")) {
+      _num_protection_domains ++;
+    }
+  }
+}
+
 size_t ArchiveHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
+  update_stats(src_obj);
+
   assert(!is_too_large_to_archive(src_obj), "already checked");
   size_t byte_size = src_obj->size() * HeapWordSize;
   assert(byte_size > 0, "no zero-size objects");
 
   // For region-based collectors such as G1, the archive heap may be mapped into
@@ -683,10 +712,23 @@
   if (ptr != nullptr) {
     NativePointerInfo info;
     info._src_obj = src_obj;
     info._field_offset = field_offset;
     _native_pointers->append(info);
+    if (!ArchiveBuilder::current()->has_been_archived((address)ptr)) {
+      // Currently we supporting marking of only Method and Klass, both of which are
+      // subtypes of MetaData.
+      ResourceMark rm;
+      log_error(cds, heap)("Native pointer %p is not archived", ptr);
+      if (((Metadata*)ptr)->is_method()) {
+        log_error(cds, heap)("Method: %s", ((Method*)ptr)->external_name());
+      } else {
+        assert(((Metadata*)ptr)->is_klass(), "must be");
+        log_error(cds, heap)("Klass: %s", ((Klass*)ptr)->external_name());
+      }
+      HeapShared::exit_on_error();
+    }
     HeapShared::set_has_native_pointers(src_obj);
     _num_native_ptrs ++;
   }
 }
 
@@ -741,10 +783,14 @@
     Metadata* native_ptr = *buffered_field_addr;
     guarantee(native_ptr != nullptr, "sanity");
     guarantee(ArchiveBuilder::current()->has_been_buffered((address)native_ptr),
               "Metadata %p should have been archived", native_ptr);
 
+    if (RegeneratedClasses::has_been_regenerated((address)native_ptr)) {
+      native_ptr = (Metadata*)RegeneratedClasses::get_regenerated_object((address)native_ptr);
+    }
+
     address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr);
     address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr);
     *buffered_field_addr = (Metadata*)requested_native_ptr;
   }
 
diff a/src/hotspot/share/cds/archiveHeapWriter.hpp b/src/hotspot/share/cds/archiveHeapWriter.hpp
--- a/src/hotspot/share/cds/archiveHeapWriter.hpp
+++ b/src/hotspot/share/cds/archiveHeapWriter.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024, 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.
@@ -60,10 +60,11 @@
   HeapRootSegments heap_root_segments() { return _heap_root_segments; }
 };
 
 #if INCLUDE_CDS_JAVA_HEAP
 class ArchiveHeapWriter : AllStatic {
+  friend class HeapShared;
   // ArchiveHeapWriter manipulates three types of addresses:
   //
   //     "source" vs "buffered" vs "requested"
   //
   // (Note: the design and convention is the same as for the archiving of Metaspace objects.
@@ -193,10 +194,11 @@
   static void root_segment_at_put(objArrayOop segment, int index, oop root);
   static objArrayOop allocate_root_segment(size_t offset, int element_count);
   static void copy_roots_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots);
   static void copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots);
   static size_t copy_one_source_obj_to_buffer(oop src_obj);
+  static void update_stats(oop src_obj);
 
   static void maybe_fill_gc_region_gap(size_t required_byte_size);
   static size_t filler_array_byte_size(int length);
   static int filler_array_length(size_t fill_bytes);
   static HeapWord* init_filler_array_at_buffer_top(int array_length, size_t fill_bytes);
diff a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp
--- a/src/hotspot/share/cds/archiveUtils.cpp
+++ b/src/hotspot/share/cds/archiveUtils.cpp
@@ -31,10 +31,11 @@
 #include "cds/classListWriter.hpp"
 #include "cds/dynamicArchive.hpp"
 #include "cds/filemap.hpp"
 #include "cds/heapShared.hpp"
 #include "cds/metaspaceShared.hpp"
+#include "classfile/classLoader.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "interpreter/bootstrapInfo.hpp"
 #include "memory/metaspaceUtils.hpp"
 #include "memory/resourceArea.hpp"
@@ -47,18 +48,20 @@
 #include "utilities/globalDefinitions.hpp"
 
 CHeapBitMap* ArchivePtrMarker::_ptrmap = nullptr;
 CHeapBitMap* ArchivePtrMarker::_rw_ptrmap = nullptr;
 CHeapBitMap* ArchivePtrMarker::_ro_ptrmap = nullptr;
+CHeapBitMap* ArchivePtrMarker::_cc_ptrmap = nullptr;
 VirtualSpace* ArchivePtrMarker::_vs;
 
 bool ArchivePtrMarker::_compacted;
 
 void ArchivePtrMarker::initialize(CHeapBitMap* ptrmap, VirtualSpace* vs) {
   assert(_ptrmap == nullptr, "initialize only once");
   assert(_rw_ptrmap == nullptr, "initialize only once");
   assert(_ro_ptrmap == nullptr, "initialize only once");
+  assert(_cc_ptrmap == nullptr, "initialize only once");
   _vs = vs;
   _compacted = false;
   _ptrmap = ptrmap;
 
   // Use this as initial guesstimate. We should need less space in the
@@ -70,39 +73,57 @@
 
   // We need one bit per pointer in the archive.
   _ptrmap->initialize(estimated_archive_size / sizeof(intptr_t));
 }
 
-void ArchivePtrMarker::initialize_rw_ro_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap) {
+void ArchivePtrMarker::initialize_rw_ro_cc_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, CHeapBitMap* cc_ptrmap) {
   address* rw_bottom = (address*)ArchiveBuilder::current()->rw_region()->base();
   address* ro_bottom = (address*)ArchiveBuilder::current()->ro_region()->base();
+  address* cc_bottom = (address*)ArchiveBuilder::current()->cc_region()->base();
 
   _rw_ptrmap = rw_ptrmap;
   _ro_ptrmap = ro_ptrmap;
+  _cc_ptrmap = cc_ptrmap;
 
   size_t rw_size = ArchiveBuilder::current()->rw_region()->used() / sizeof(address);
   size_t ro_size = ArchiveBuilder::current()->ro_region()->used() / sizeof(address);
+  size_t cc_size = ArchiveBuilder::current()->cc_region()->used() / sizeof(address);
   // ro_start is the first bit in _ptrmap that covers the pointer that would sit at ro_bottom.
   // E.g., if rw_bottom = (address*)100
   //          ro_bottom = (address*)116
   //       then for 64-bit platform:
   //          ro_start = ro_bottom - rw_bottom = (116 - 100) / sizeof(address) = 2;
   size_t ro_start = ro_bottom - rw_bottom;
+  size_t cc_start = cc_bottom - rw_bottom;
 
-  // Note: ptrmap is big enough only to cover the last pointer in ro_region.
+  // Note: ptrmap is big enough only to cover the last pointer in cc_region or ro_region.
   // See ArchivePtrMarker::compact()
+  if (ro_start + ro_size >_ptrmap->size()) {
+    ro_size = _ptrmap->size() - ro_start; // ro is smaller than we thought
+    cc_size = 0;                          // cc is empty
+  } else if (cc_size != 0 && cc_start + cc_size > _ptrmap->size()) {
+    cc_size = _ptrmap->size() - cc_start; // ro is smaller than we thought
+  }
+
+  assert(rw_size < _ptrmap->size(), "sanity");
+  assert(ro_size < _ptrmap->size(), "sanity");
+  assert(cc_size < _ptrmap->size(), "sanity");
+  assert(rw_size + ro_size + cc_size <= _ptrmap->size(), "sanity");
+
   _rw_ptrmap->initialize(rw_size);
-  _ro_ptrmap->initialize(_ptrmap->size() - ro_start);
+  _ro_ptrmap->initialize(ro_size);
+  _cc_ptrmap->initialize(cc_size);
 
-  for (size_t rw_bit = 0; rw_bit < _rw_ptrmap->size(); rw_bit++) {
-    _rw_ptrmap->at_put(rw_bit, _ptrmap->at(rw_bit));
+  for (size_t i = 0; i < rw_size; i++) {
+    _rw_ptrmap->at_put(i, _ptrmap->at(i));
   }
-
-  for(size_t ro_bit = ro_start; ro_bit < _ptrmap->size(); ro_bit++) {
-    _ro_ptrmap->at_put(ro_bit-ro_start, _ptrmap->at(ro_bit));
+  for (size_t i = 0; i < ro_size; i++) {
+    _ro_ptrmap->at_put(i, _ptrmap->at(ro_start + i));
+  }
+  for (size_t i = 0; i < cc_size; i++) {
+    _cc_ptrmap->at_put(i, _ptrmap->at(cc_start + i));
   }
-  assert(_ptrmap->size() - ro_start == _ro_ptrmap->size(), "must be");
 }
 
 void ArchivePtrMarker::mark_pointer(address* ptr_loc) {
   assert(_ptrmap != nullptr, "not initialized");
   assert(!_compacted, "cannot mark anymore");
@@ -262,13 +283,14 @@
     ArchivePtrMarker::mark_pointer(p);
   }
 }
 
 void DumpRegion::print(size_t total_bytes) const {
+  char* base = used() > 0 ? ArchiveBuilder::current()->to_requested(_base) : nullptr;
   log_debug(cds)("%s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT,
                  _name, used(), percent_of(used(), total_bytes), reserved(), percent_of(used(), reserved()),
-                 p2i(ArchiveBuilder::current()->to_requested(_base)));
+                 p2i(base));
 }
 
 void DumpRegion::print_out_of_space_msg(const char* failing_region, size_t needed_bytes) {
   log_error(cds)("[%-8s] " PTR_FORMAT " - " PTR_FORMAT " capacity =%9d, allocated =%9d",
                  _name, p2i(_base), p2i(_top), int(_end - _base), int(_top - _base));
@@ -287,13 +309,14 @@
   _base = _top = _rs->base();
   _end = _rs->end();
 }
 
 void DumpRegion::pack(DumpRegion* next) {
-  assert(!is_packed(), "sanity");
-  _end = (char*)align_up(_top, MetaspaceShared::core_region_alignment());
-  _is_packed = true;
+  if (!is_packed()) {
+    _end = (char*)align_up(_top, MetaspaceShared::core_region_alignment());
+    _is_packed = true;
+  }
   if (next != nullptr) {
     next->_rs = _rs;
     next->_vs = _vs;
     next->_base = next->_top = this->_end;
     next->_end = _rs->end();
@@ -369,10 +392,60 @@
       }
     }
   }
 }
 
+
+// "boot", "platform", "app" or nullptr
+const char* ArchiveUtils::builtin_loader_name_or_null(oop loader) {
+  if (loader == nullptr) {
+    return "boot";
+  } else if (loader == SystemDictionary::java_platform_loader()) {
+    return "platform";
+  } else if (loader == SystemDictionary::java_system_loader()) {
+    return "app";
+  } else {
+    return nullptr;
+  }
+}
+
+// "boot", "platform", "app". Asserts if not a built-in-loader
+const char* ArchiveUtils::builtin_loader_name(oop loader) {
+  const char* name = builtin_loader_name_or_null(loader);
+  assert(name != nullptr, "must be a built-in loader");
+  return name;
+}
+
+bool ArchiveUtils::builtin_loader_from_type(const char* loader_type, oop* value_ret) {
+  if (strcmp(loader_type, "boot") == 0) {
+    *value_ret = nullptr;
+    return true;
+  } else if (strcmp(loader_type, "platform") == 0) {
+    *value_ret = SystemDictionary::java_platform_loader();
+    return true;
+  } else if (strcmp(loader_type, "app") == 0) {
+    *value_ret = SystemDictionary::java_system_loader();
+    return true;
+  } else {
+    DEBUG_ONLY(*value_ret = cast_to_oop((void*)badOopVal));
+    return false;
+  }
+}
+
+oop ArchiveUtils::builtin_loader_from_type(int loader_type) {
+  if (loader_type == ClassLoader::BOOT_LOADER) {
+    return nullptr;
+  } else if (loader_type == ClassLoader::PLATFORM_LOADER)  {
+    return SystemDictionary::java_platform_loader();
+  } else if (loader_type == ClassLoader::APP_LOADER) {
+    return SystemDictionary::java_system_loader();
+  } else {
+    ShouldNotReachHere();
+    return nullptr;
+  }
+}
+
 bool ArchiveUtils::has_aot_initialized_mirror(InstanceKlass* src_ik) {
   if (SystemDictionaryShared::is_excluded_class(src_ik)) {
     assert(!ArchiveBuilder::current()->has_been_buffered(src_ik), "sanity");
     return false;
   }
@@ -396,6 +469,5 @@
 
 size_t HeapRootSegments::segment_offset(size_t seg_idx) {
   assert(seg_idx < _count, "In range");
   return _base_offset + seg_idx * _max_size_in_bytes;
 }
-
diff a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp
--- a/src/hotspot/share/cds/archiveUtils.hpp
+++ b/src/hotspot/share/cds/archiveUtils.hpp
@@ -47,10 +47,11 @@
 // fixed, but _ptr_end can be expanded as more objects are dumped.
 class ArchivePtrMarker : AllStatic {
   static CHeapBitMap*  _ptrmap;
   static CHeapBitMap*  _rw_ptrmap;
   static CHeapBitMap*  _ro_ptrmap;
+  static CHeapBitMap*  _cc_ptrmap;
   static VirtualSpace* _vs;
 
   // Once _ptrmap is compacted, we don't allow bit marking anymore. This is to
   // avoid unintentional copy operations after the bitmap has been finalized and written.
   static bool         _compacted;
@@ -58,11 +59,11 @@
   static address* ptr_base() { return (address*)_vs->low();  } // committed lower bound (inclusive)
   static address* ptr_end()  { return (address*)_vs->high(); } // committed upper bound (exclusive)
 
 public:
   static void initialize(CHeapBitMap* ptrmap, VirtualSpace* vs);
-  static void initialize_rw_ro_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap);
+  static void initialize_rw_ro_cc_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, CHeapBitMap* cc_ptrmap);
   static void mark_pointer(address* ptr_loc);
   static void clear_pointer(address* ptr_loc);
   static void compact(address relocatable_base, address relocatable_end);
   static void compact(size_t max_non_null_offset);
 
@@ -87,14 +88,19 @@
 
   static CHeapBitMap* ro_ptrmap() {
     return _ro_ptrmap;
   }
 
+  static CHeapBitMap* cc_ptrmap() {
+    return _cc_ptrmap;
+  }
+
   static void reset_map_and_vs() {
     _ptrmap = nullptr;
     _rw_ptrmap = nullptr;
     _ro_ptrmap = nullptr;
+    _cc_ptrmap = nullptr;
     _vs = nullptr;
   }
 };
 
 // SharedDataRelocator is used to shift pointers in the CDS archive.
@@ -257,10 +263,16 @@
   static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF;
   static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN;
   static bool has_aot_initialized_mirror(InstanceKlass* src_ik);
   template <typename T> static Array<T>* archive_array(GrowableArray<T>* tmp_array);
 
+  static const char* builtin_loader_name_or_null(oop loader); // "boot", "platform", "app", or nullptr
+  static const char* builtin_loader_name(oop loader); // "boot", "platform", or "app". Asserts if not a built-in-loader.
+
+  static bool builtin_loader_from_type(const char* loader_type, oop* value_ret);
+  static oop builtin_loader_from_type(int loader_type);
+
   // offset must represent an object of type T in the mapped shared space. Return
   // a direct pointer to this object.
   template <typename T> T static from_offset(u4 offset) {
     T p = (T)(SharedBaseAddress + offset);
     assert(Metaspace::is_in_shared_metaspace(p), "must be");
diff a/src/hotspot/share/cds/cdsAccess.cpp b/src/hotspot/share/cds/cdsAccess.cpp
--- /dev/null
+++ b/src/hotspot/share/cds/cdsAccess.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "cds/archiveBuilder.hpp"
+#include "cds/cdsAccess.hpp"
+#include "cds/cdsConfig.hpp"
+#include "cds/filemap.hpp"
+#include "cds/heapShared.hpp"
+#include "cds/metaspaceShared.hpp"
+#include "classfile/stringTable.hpp"
+#include "logging/log.hpp"
+#include "logging/logStream.hpp"
+#include "memory/resourceArea.hpp"
+#include "memory/universe.hpp"
+#include "memory/virtualspace.hpp"
+#include "oops/instanceKlass.hpp"
+
+size_t _cached_code_size = 0;
+
+bool CDSAccess::can_generate_cached_code(address addr) {
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    return ArchiveBuilder::is_active() && ArchiveBuilder::current()->has_been_archived(addr);
+  } else {
+    // Old CDS+AOT workflow.
+    return MetaspaceShared::is_in_shared_metaspace(addr);
+  }
+}
+
+bool CDSAccess::can_generate_cached_code(InstanceKlass* ik) {
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    if (!ArchiveBuilder::is_active()) {
+      return false;
+    }
+    ArchiveBuilder* builder = ArchiveBuilder::current();
+    if (!builder->has_been_archived((address)ik)) {
+      return false;
+    }
+    InstanceKlass* buffered_ik = builder->get_buffered_addr(ik);
+    if (ik->is_shared_unregistered_class()) {
+      return false;
+    }
+    return true;
+  } else {
+    // Old CDS+AOT workflow.
+    return ik->is_shared() && !ik->is_shared_unregistered_class();
+  }
+}
+
+uint CDSAccess::delta_from_shared_address_base(address addr) {
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    assert(ArchiveBuilder::is_active(), "must be");
+    ArchiveBuilder* builder = ArchiveBuilder::current();
+    address requested_addr = builder->to_requested(builder->get_buffered_addr(addr));
+    return (uint)pointer_delta(requested_addr, (address)SharedBaseAddress, 1);
+  } else {
+    // Old CDS+AOT workflow.
+    return (uint)pointer_delta(addr, (address)SharedBaseAddress, 1);
+  }
+}
+
+Method* CDSAccess::method_in_cached_code(Method* m) {
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    assert(ArchiveBuilder::is_active(), "must be");
+    ArchiveBuilder* builder = ArchiveBuilder::current();
+    return builder->to_requested(builder->get_buffered_addr(m));
+  } else {
+    // Old CDS+AOT workflow.
+    return m;
+  }
+}
+
+#if INCLUDE_CDS_JAVA_HEAP
+int CDSAccess::get_archived_object_permanent_index(oop obj) {
+  return HeapShared::get_archived_object_permanent_index(obj);
+}
+
+oop CDSAccess::get_archived_object(int permanent_index) {
+  return HeapShared::get_archived_object(permanent_index);
+}
+
+static void test_cds_heap_access_api_for_object(oop obj) {
+  LogStreamHandle(Info, cds, jit) log;
+
+  obj->print_on(&log);
+  log.cr();
+
+  int n = CDSAccess::get_archived_object_permanent_index(obj); // call this when -XX:+StoreCachedCode
+  if (n < 0) {
+    log.print_cr("*** This object is not in CDS archive");
+  } else {
+    log.print_cr("CDSAccess::get_archived_object_permanent_index(s) = %d", n);
+    oop archived_obj = CDSAccess::get_archived_object(n); // call this when -XX:+LoadCachedCode
+    if (archived_obj == obj || archived_obj == HeapShared::orig_to_scratch_object(obj)) {
+      log.print_cr("CDSAccess::get_archived_object(%d) returns the same object, as expected", n);
+    } else {
+      log.print_cr("Error!!! CDSAccess::get_archived_object(%d) returns an unexpected object", n);
+      if (archived_obj == nullptr) {
+        log.print_cr("--> null");
+      } else {
+        archived_obj->print_on(&log);
+        log.cr();
+      }
+    }
+  }
+}
+
+// TEMP: examples for using the CDSAccess::get_archived_object_permanent_index() and CDSAccess::get_archived_object()
+// APIs for the AOT compiler.
+
+void CDSAccess::test_heap_access_api() {
+  ResourceMark rm;
+  const char* tests[] = {
+    "",
+    "null",
+    "NARROW",
+    "not in cds",
+    nullptr,
+  };
+
+  LogStreamHandle(Info, cds, jit) log;
+
+  int i;
+  for (i = 0; tests[i] != nullptr; i++) {
+    EXCEPTION_MARK;
+    log.print_cr("Test %d ======================================== \"%s\"", i, tests[i]);
+    oop s = StringTable::intern(tests[i], CHECK);
+    test_cds_heap_access_api_for_object(s);
+  }
+
+  log.print_cr("Test %d ======================================== Universe::null_ptr_exception_instance()", i);
+  test_cds_heap_access_api_for_object(Universe::null_ptr_exception_instance());
+}
+
+// new workflow only
+void* CDSAccess::allocate_from_code_cache(size_t size) {
+  assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+  return (void*)ArchiveBuilder::cc_region_alloc(size);
+}
+
+size_t CDSAccess::get_cached_code_size() {
+  return _cached_code_size;
+}
+
+void CDSAccess::set_cached_code_size(size_t sz) {
+  _cached_code_size = sz;
+}
+
+void CDSAccess::set_pointer(address* ptr, address value) {
+  ArchiveBuilder* builder = ArchiveBuilder::current();
+  if (value != nullptr && !builder->is_in_buffer_space(value)) {
+    value = builder->get_buffered_addr(value);
+  }
+  *ptr = value;
+  ArchivePtrMarker::mark_pointer(ptr);
+}
+
+bool CDSAccess::map_cached_code(ReservedSpace rs) {
+  FileMapInfo* static_mapinfo = FileMapInfo::current_info();
+  assert(UseSharedSpaces && static_mapinfo != nullptr, "must be");
+  return static_mapinfo->map_cached_code_region(rs);
+}
+
+#endif // INCLUDE_CDS_JAVA_HEAP
diff a/src/hotspot/share/cds/cdsAccess.hpp b/src/hotspot/share/cds/cdsAccess.hpp
--- /dev/null
+++ b/src/hotspot/share/cds/cdsAccess.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#ifndef SHARE_CDS_CDSACCESS_HPP
+#define SHARE_CDS_CDSACCESS_HPP
+
+#include "cds/archiveBuilder.hpp"
+#include "cds/archiveUtils.hpp"
+#include "memory/allStatic.hpp"
+#include "oops/oopsHierarchy.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class InstanceKlass;
+class Klass;
+class Method;
+class ReservedSpace;
+
+class CDSAccess : AllStatic {
+private:
+  static bool can_generate_cached_code(address addr) NOT_CDS_RETURN_(false);
+public:
+  static bool can_generate_cached_code(Method* m) {
+    return can_generate_cached_code((address)m);
+  }
+  static bool can_generate_cached_code(Klass* k) {
+    return can_generate_cached_code((address)k);
+  }
+  static bool can_generate_cached_code(InstanceKlass* ik) NOT_CDS_RETURN_(false);
+
+  static uint delta_from_shared_address_base(address addr);
+  static Method* method_in_cached_code(Method* m) NOT_CDS_RETURN_(nullptr);
+
+  static int get_archived_object_permanent_index(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(-1);
+  static oop get_archived_object(int permanent_index) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
+
+  static void test_heap_access_api() NOT_CDS_JAVA_HEAP_RETURN;
+
+  static void* allocate_from_code_cache(size_t size) NOT_CDS_RETURN_(nullptr);
+
+  static size_t get_cached_code_size() NOT_CDS_JAVA_HEAP_RETURN_(0);
+  static void set_cached_code_size(size_t sz) NOT_CDS_JAVA_HEAP_RETURN;
+
+  static bool map_cached_code(ReservedSpace rs) NOT_CDS_JAVA_HEAP_RETURN_(false);
+
+  template <typename T>
+  static void set_pointer(T** ptr, T* value) {
+    set_pointer((address*)ptr, (address)value);
+  }
+  static void set_pointer(address* ptr, address value);
+};
+
+#endif // SHARE_CDS_CDSACCESS_HPP
diff a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp
--- a/src/hotspot/share/cds/cdsConfig.cpp
+++ b/src/hotspot/share/cds/cdsConfig.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025, 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.
@@ -23,16 +23,20 @@
  */
 
 #include "precompiled.hpp"
 #include "cds/archiveHeapLoader.hpp"
 #include "cds/cdsConfig.hpp"
+#include "cds/cds_globals.hpp"
 #include "cds/classListWriter.hpp"
 #include "cds/heapShared.hpp"
+#include "cds/metaspaceShared.hpp"
 #include "classfile/classLoaderDataShared.hpp"
 #include "classfile/moduleEntry.hpp"
+#include "classfile/systemDictionaryShared.hpp"
 #include "include/jvm_io.h"
 #include "logging/log.hpp"
+#include "prims/jvmtiExport.hpp"
 #include "memory/universe.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/globals_extension.hpp"
 #include "runtime/java.hpp"
 #include "runtime/vmThread.hpp"
@@ -44,10 +48,13 @@
 bool CDSConfig::_is_using_optimized_module_handling = true;
 bool CDSConfig::_is_dumping_full_module_graph = true;
 bool CDSConfig::_is_using_full_module_graph = true;
 bool CDSConfig::_has_aot_linked_classes = false;
 bool CDSConfig::_has_archived_invokedynamic = false;
+bool CDSConfig::_is_loading_packages = false;
+bool CDSConfig::_is_loading_protection_domains = false;
+bool CDSConfig::_is_security_manager_allowed = false;
 bool CDSConfig::_old_cds_flags_used = false;
 
 char* CDSConfig::_default_archive_path = nullptr;
 char* CDSConfig::_static_archive_path = nullptr;
 char* CDSConfig::_dynamic_archive_path = nullptr;
@@ -57,18 +64,19 @@
 int CDSConfig::get_status() {
   assert(Universe::is_fully_initialized(), "status is finalized only after Universe is initialized");
   return (is_dumping_archive()              ? IS_DUMPING_ARCHIVE : 0) |
          (is_dumping_static_archive()       ? IS_DUMPING_STATIC_ARCHIVE : 0) |
          (is_logging_lambda_form_invokers() ? IS_LOGGING_LAMBDA_FORM_INVOKERS : 0) |
-         (is_using_archive()                ? IS_USING_ARCHIVE : 0);
+         (is_using_archive()                ? IS_USING_ARCHIVE : 0) |
+         (is_dumping_heap()                 ? IS_DUMPING_HEAP : 0) |
+         (is_logging_dynamic_proxies()      ? IS_LOGGING_DYNAMIC_PROXIES : 0) |
+         (is_dumping_packages()             ? IS_DUMPING_PACKAGES : 0) |
+         (is_dumping_protection_domains()   ? IS_DUMPING_PROTECTION_DOMAINS : 0);
 }
 
 void CDSConfig::initialize() {
-  if (is_dumping_static_archive()) {
-    if (RequireSharedSpaces) {
-      warning("Cannot dump shared archive while using shared archive");
-    }
+  if (is_dumping_static_archive() && !is_dumping_final_static_archive()) {
     UseSharedSpaces = false;
   }
 
   // Initialize shared archive paths which could include both base and dynamic archive paths
   // This must be after set_ergonomics_flags() called so flag UseCompressedOops is set properly.
@@ -146,10 +154,17 @@
   cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
   strncpy(cur_path, begin_ptr, len + 1);
   *top_archive_path = cur_path;
 }
 
+static void set_new_workflow_default_CachedCodeFile() {
+  size_t len = strlen(CacheDataStore) + 6;
+  char* file = AllocateHeap(len, mtArguments);
+  jio_snprintf(file, len, "%s.code", CacheDataStore);
+  FLAG_SET_ERGO(CachedCodeFile, file);
+}
+
 void CDSConfig::init_shared_archive_paths() {
   if (ArchiveClassesAtExit != nullptr) {
     assert(!RecordDynamicDumpInfo, "already checked");
     if (is_dumping_static_archive()) {
       vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump");
@@ -171,10 +186,14 @@
     if (is_dumping_archive() && archives > 1) {
       vm_exit_during_initialization(
         "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping");
     }
 
+    if (CDSPreimage != nullptr && archives > 1) {
+      vm_exit_during_initialization("CDSPreimage must point to a single file", CDSPreimage);
+    }
+
     if (is_dumping_static_archive()) {
       assert(archives == 1, "must be");
       // Static dump is simple: only one archive is allowed in SharedArchiveFile. This file
       // will be overwritten no matter regardless of its contents
       _static_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments);
@@ -195,10 +214,14 @@
       if (archives == 1) {
         char* base_archive_path = nullptr;
         bool success =
           FileMapInfo::get_base_archive_name_from_header(SharedArchiveFile, &base_archive_path);
         if (!success) {
+          if (CDSPreimage != nullptr) {
+            vm_exit_during_initialization("Unable to map shared spaces from CDSPreimage", CDSPreimage);
+          }
+
           // If +AutoCreateSharedArchive and the specified shared archive does not exist,
           // regenerate the dynamic archive base on default archive.
           if (AutoCreateSharedArchive && !os::file_exists(SharedArchiveFile)) {
             enable_dumping_dynamic_archive();
             ArchiveClassesAtExit = const_cast<char *>(SharedArchiveFile);
@@ -247,16 +270,28 @@
       }
     }
   }
 }
 
+static char* bad_module_prop_key   = nullptr;
+static char* bad_module_prop_value = nullptr;
+
 void CDSConfig::check_internal_module_property(const char* key, const char* value) {
   if (Arguments::is_internal_module_property(key) &&
       !Arguments::is_module_path_property(key) &&
       !Arguments::is_add_modules_property(key)) {
     stop_using_optimized_module_handling();
-    log_info(cds)("optimized module handling: disabled due to incompatible property: %s=%s", key, value);
+    if (bad_module_prop_key == nullptr) {
+      // We don't want to print an unconditional warning here, as we are still processing the command line.
+      // A later argument may specify something like -Xshare:off, which makes such a warning irrelevant.
+      //
+      // Instead, we save the info so we can warn when necessary: we are doing it only during CacheDataStore
+      // creation for now, but could add it to other places.
+      bad_module_prop_key   = os::strdup(key);
+      bad_module_prop_value = os::strdup(value);
+    }
+    log_info(cds)("optimized module handling/full module graph: disabled due to incompatible property: %s=%s", key, value);
   }
 }
 
 void CDSConfig::check_incompatible_property(const char* key, const char* value) {
   static const char* incompatible_properties[] = {
@@ -272,10 +307,16 @@
       log_info(cds)("full module graph: disabled due to incompatible property: %s=%s", key, value);
       break;
     }
   }
 
+  // Match the logic in java/lang/System.java, but we need to know this before the System class is initialized.
+  if (strcmp(key, "java.security.manager") == 0) {
+    if (strcmp(value, "disallowed") != 0) {
+      _is_security_manager_allowed = true;
+    }
+  }
 }
 
 // Returns any JVM command-line option, such as "--patch-module", that's not supported by CDS.
 static const char* find_any_unsupported_module_option() {
   // Note that arguments.cpp has translated the command-line options into properties. If we find an
@@ -417,23 +458,155 @@
       CDSConfig::enable_dumping_static_archive();
     }
   }
 }
 
-bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line) {
+bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line, bool xshare_auto_cmd_line) {
   check_flag_aliases();
 
+  if (CacheDataStore != nullptr) {
+    // Leyden temp work-around:
+    //
+    // By default, when using CacheDataStore, use the HeapBasedNarrowOop mode so that
+    // AOT code can be always work regardless of runtime heap range.
+    //
+    // If you are *absolutely sure* that the CompressedOops::mode() will be the same
+    // between training and production runs (e.g., if you specify -Xmx128m
+    // for both training and production runs, and you know the OS will always reserve
+    // the heap under 4GB), you can explicitly disable this with:
+    //     java -XX:-UseCompatibleCompressedOops -XX:CacheDataStore=...
+    // However, this is risky and there's a chance that the production run will be slower
+    // because it is unable to load the AOT code cache.
+#ifdef _LP64
+    FLAG_SET_ERGO_IF_DEFAULT(UseCompatibleCompressedOops, true);
+#endif
+
+    // Leyden temp: make sure the user knows if CDS archive somehow fails to load.
+    if (UseSharedSpaces && !xshare_auto_cmd_line) {
+      log_info(cds)("Enabled -Xshare:on by default for troubleshooting Leyden prototype");
+      RequireSharedSpaces = true;
+    }
+
+    if (FLAG_IS_DEFAULT(AOTClassLinking)) {
+      // New workflow - enable AOTClassLinking by default.
+      // TODO: make new workflow work, even when AOTClassLinking is false.
+      //
+      // NOTE: in old workflow, we cannot enable AOTClassLinking by default. That
+      // should be an opt-in option, per JEP nnn.
+      FLAG_SET_ERGO(AOTClassLinking, true);
+    }
+
+    if (SharedArchiveFile != nullptr) {
+      vm_exit_during_initialization("CacheDataStore and SharedArchiveFile cannot be both specified");
+    }
+    if (!AOTClassLinking) {
+      // TODO: in the forked JVM, we should ensure all classes are loaded from the hotspot.cds.preimage.
+      // AOTClassLinking only loads the classes for built-in loaders. We need to load the classes
+      // for custom loaders as well.
+      vm_exit_during_initialization("CacheDataStore requires AOTClassLinking");
+    }
+
+    if (CDSPreimage == nullptr) {
+      if (os::file_exists(CacheDataStore) /* && TODO: CDS file is valid*/) {
+        // The CacheDataStore is already up to date. Use it. Also turn on cached code by default.
+        SharedArchiveFile = CacheDataStore;
+        FLAG_SET_ERGO_IF_DEFAULT(ReplayTraining, true);
+        FLAG_SET_ERGO_IF_DEFAULT(LoadCachedCode, true);
+        if (LoadCachedCode && FLAG_IS_DEFAULT(CachedCodeFile)) {
+          set_new_workflow_default_CachedCodeFile();
+        }
+      } else {
+        // The preimage dumping phase -- run the app and write the preimage file
+        size_t len = strlen(CacheDataStore) + 10;
+        char* preimage = AllocateHeap(len, mtArguments);
+        jio_snprintf(preimage, len, "%s.preimage", CacheDataStore);
+
+        UseSharedSpaces = false;
+        enable_dumping_static_archive();
+        SharedArchiveFile = preimage;
+        log_info(cds)("CacheDataStore needs to be updated. Writing %s file", SharedArchiveFile);
+
+        // At VM exit, the module graph may be contaminated with program states. We should rebuild the
+        // module graph when dumping the CDS final image.
+        log_info(cds)("full module graph: disabled when writing CDS preimage");
+        HeapShared::disable_writing();
+        stop_dumping_full_module_graph();
+        FLAG_SET_ERGO(ArchivePackages, false);
+        FLAG_SET_ERGO(ArchiveProtectionDomains, false);
+
+        FLAG_SET_ERGO_IF_DEFAULT(RecordTraining, true);
+      }
+    } else {
+      // The final image dumping phase -- load the preimage and write the final image file
+      SharedArchiveFile = CDSPreimage;
+      UseSharedSpaces = true;
+      log_info(cds)("Generate CacheDataStore %s from CDSPreimage %s", CacheDataStore, CDSPreimage);
+      // Force -Xbatch for AOT compilation.
+      if (FLAG_SET_CMDLINE(BackgroundCompilation, false) != JVMFlag::SUCCESS) {
+        return false;
+      }
+      RecordTraining = false; // This will be updated inside MetaspaceShared::preload_and_dump()
+
+      FLAG_SET_ERGO_IF_DEFAULT(ReplayTraining, true);
+      // Settings for AOT
+      FLAG_SET_ERGO_IF_DEFAULT(StoreCachedCode, true);
+      if (StoreCachedCode && FLAG_IS_DEFAULT(CachedCodeFile)) {
+        set_new_workflow_default_CachedCodeFile();
+        // Cannot dump cached code until metadata and heap are dumped.
+        disable_dumping_cached_code();
+      }
+    }
+  } else {
+    // Old workflow
+    if (CDSPreimage != nullptr) {
+      vm_exit_during_initialization("CDSPreimage must be specified only when CacheDataStore is specified");
+    }
+  }
+
+  if (FLAG_IS_DEFAULT(UsePermanentHeapObjects)) {
+    if (StoreCachedCode || AOTClassLinking) {
+      FLAG_SET_ERGO(UsePermanentHeapObjects, true);
+    }
+  }
+
+  if (LoadCachedCode) {
+    // This must be true. Cached code is hard-wired to use permanent objects.
+    UsePermanentHeapObjects = true;
+  }
+
   if (AOTClassLinking) {
-    // If AOTClassLinking is specified, enable all AOT optimizations by default.
+    // If AOTClassLinking is specified, enable all these optimizations by default.
     FLAG_SET_ERGO_IF_DEFAULT(AOTInvokeDynamicLinking, true);
+    FLAG_SET_ERGO_IF_DEFAULT(ArchiveDynamicProxies, true);
+    FLAG_SET_ERGO_IF_DEFAULT(ArchiveLoaderLookupCache, true);
+    FLAG_SET_ERGO_IF_DEFAULT(ArchivePackages, true);
+    FLAG_SET_ERGO_IF_DEFAULT(ArchiveProtectionDomains, true);
+    FLAG_SET_ERGO_IF_DEFAULT(ArchiveReflectionData, true);
+
+    FLAG_SET_ERGO(ArchiveDynamicProxies, false);     // FIXME -- leyden+JEP483 merge
+    FLAG_SET_ERGO(ArchiveLoaderLookupCache, false);  // FIXME -- leyden+JEP483 merge
+    FLAG_SET_ERGO(ArchiveReflectionData, false);     // FIXME -- leyden+JEP483 merge
   } else {
-    // AOTInvokeDynamicLinking depends on AOTClassLinking.
+    // All of these *might* depend on AOTClassLinking. Better be safe than sorry.
+    // TODO: more fine-grained handling.
     FLAG_SET_ERGO(AOTInvokeDynamicLinking, false);
+    FLAG_SET_ERGO(ArchiveDynamicProxies, false);
+    FLAG_SET_ERGO(ArchiveLoaderLookupCache, false);
+    FLAG_SET_ERGO(ArchivePackages, false);
+    FLAG_SET_ERGO(ArchiveProtectionDomains, false);
+    FLAG_SET_ERGO(ArchiveReflectionData, false);
   }
 
+#ifdef _WINDOWS
+  // This optimization is not working on Windows for some reason. See JDK-8338604.
+  FLAG_SET_ERGO(ArchiveReflectionData, false);
+#endif
+
   if (is_dumping_static_archive()) {
-    if (!mode_flag_cmd_line) {
+    if (is_dumping_preimage_static_archive() || is_dumping_final_static_archive()) {
+      // Don't tweak execution mode
+    } else if (!mode_flag_cmd_line) {
       // By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive.
       //
       // If your classlist is large and you don't care about deterministic dumping, you can use
       // -Xshare:dump -Xmixed to improve dumping speed.
       Arguments::set_mode_flags(Arguments::_int);
@@ -490,24 +663,81 @@
       BytecodeVerificationRemote = true;
       log_info(cds)("All non-system classes will be verified (-Xverify:remote) during CDS dump time.");
     }
   }
 
+  if (AOTClassLinking) {
+    if ((is_dumping_preimage_static_archive() && !is_using_optimized_module_handling()) ||
+        (is_dumping_final_static_archive()    && !is_dumping_full_module_graph())) {
+      if (bad_module_prop_key != nullptr) {
+        log_warning(cds)("optimized module handling/full module graph: disabled due to incompatible property: %s=%s",
+                         bad_module_prop_key, bad_module_prop_value);
+      }
+      vm_exit_during_initialization("CacheDataStore cannot be created because AOTClassLinking is enabled but full module graph is disabled");
+    }
+  }
+
   return true;
 }
 
+bool CDSConfig::is_dumping_classic_static_archive() {
+  return _is_dumping_static_archive && CacheDataStore == nullptr && CDSPreimage == nullptr;
+}
+
+bool CDSConfig::is_dumping_preimage_static_archive() {
+  return _is_dumping_static_archive && CacheDataStore != nullptr && CDSPreimage == nullptr;
+}
+
+bool CDSConfig::is_dumping_preimage_static_archive_with_triggers() {
+  return (!FLAG_IS_DEFAULT(AOTEndTrainingOnMethodEntry)) && is_dumping_preimage_static_archive();
+}
+
+bool CDSConfig::is_dumping_final_static_archive() {
+  if (CDSPreimage != nullptr) {
+    assert(CacheDataStore != nullptr, "must be"); // should have been properly initialized by arguments.cpp
+  }
+
+  // Note: _is_dumping_static_archive is false! // FIXME -- refactor this so it makes more sense!
+  return CacheDataStore != nullptr && CDSPreimage != nullptr;
+}
+
 bool CDSConfig::allow_only_single_java_thread() {
   // See comments in JVM_StartThread()
-  return is_dumping_static_archive();
+  return is_dumping_classic_static_archive() || is_dumping_final_static_archive();
+}
+
+bool CDSConfig::is_dumping_regenerated_lambdaform_invokers() {
+  if (is_dumping_final_static_archive()) {
+    // Not yet supported in new workflow -- the training data may point
+    // to a method in a lambdaform holder class that was not regenerated
+    // due to JDK-8318064.
+    return false;
+  } else {
+    return is_dumping_archive();
+  }
+}
+
+bool CDSConfig::is_logging_dynamic_proxies() {
+  return ClassListWriter::is_enabled() || is_dumping_preimage_static_archive();
+}
+
+// Preserve all states that were examined used during dumptime verification, such
+// that the verification result (pass or fail) cannot be changed at runtime.
+//
+// For example, if the verification of ik requires that class A must be a subtype of B,
+// then this relationship between A and B cannot be changed at runtime. I.e., the app
+// cannot load alternative versions of A and B such that A is not a subtype of B.
+bool CDSConfig::preserve_all_dumptime_verification_states(const InstanceKlass* ik) {
+  return is_dumping_aot_linked_classes() && SystemDictionaryShared::is_builtin(ik);
 }
 
 bool CDSConfig::is_using_archive() {
   return UseSharedSpaces;
 }
 
 bool CDSConfig::is_logging_lambda_form_invokers() {
-  return ClassListWriter::is_enabled() || is_dumping_dynamic_archive();
+  return ClassListWriter::is_enabled() || is_dumping_dynamic_archive() || is_dumping_preimage_static_archive();
 }
 
 void CDSConfig::stop_using_optimized_module_handling() {
   _is_using_optimized_module_handling = false;
   _is_dumping_full_module_graph = false; // This requires is_using_optimized_module_handling()
@@ -530,12 +760,12 @@
   return t != nullptr && (t->is_VM_thread() || t == _dumper_thread);
 }
 
 #if INCLUDE_CDS_JAVA_HEAP
 bool CDSConfig::is_dumping_heap() {
-  // heap dump is not supported in dynamic dump
-  return is_dumping_static_archive() && HeapShared::can_write();
+  return is_dumping_static_archive() && !is_dumping_preimage_static_archive()
+    && HeapShared::can_write();
 }
 
 bool CDSConfig::is_loading_heap() {
   return ArchiveHeapLoader::is_in_use();
 }
@@ -578,25 +808,36 @@
     }
   }
 }
 
 bool CDSConfig::is_dumping_aot_linked_classes() {
-  if (is_dumping_dynamic_archive()) {
+  if (is_dumping_preimage_static_archive()) {
+    return AOTClassLinking;
+  } else if (is_dumping_dynamic_archive()) {
     return is_using_full_module_graph() && AOTClassLinking;
   } else if (is_dumping_static_archive()) {
     return is_dumping_full_module_graph() && AOTClassLinking;
   } else {
     return false;
   }
 }
 
 bool CDSConfig::is_using_aot_linked_classes() {
+  if (is_dumping_final_static_archive()) {
+    // We assume that the final image is being dumped with the exact same module graph as the training run,
+    // so all aot-linked classes can be loaded.
+    return _has_aot_linked_classes;
+  }
   // Make sure we have the exact same module graph as in the assembly phase, or else
   // some aot-linked classes may not be visible so cannot be loaded.
   return is_using_full_module_graph() && _has_aot_linked_classes;
 }
 
+bool CDSConfig::is_dumping_dynamic_proxies() {
+  return is_dumping_full_module_graph() && is_dumping_invokedynamic() && ArchiveDynamicProxies;
+}
+
 void CDSConfig::set_has_aot_linked_classes(bool has_aot_linked_classes) {
   _has_aot_linked_classes |= has_aot_linked_classes;
 }
 
 bool CDSConfig::is_initing_classes_at_dump_time() {
@@ -607,10 +848,57 @@
   // Requires is_dumping_aot_linked_classes(). Otherwise the classes of some archived heap
   // objects used by the archive indy callsites may be replaced at runtime.
   return AOTInvokeDynamicLinking && is_dumping_aot_linked_classes() && is_dumping_heap();
 }
 
+bool CDSConfig::is_dumping_packages() {
+  return ArchivePackages && is_dumping_heap();
+}
+
+bool CDSConfig::is_loading_packages() {
+  return UseSharedSpaces && is_using_full_module_graph() && _is_loading_packages;
+}
+
+bool CDSConfig::is_dumping_protection_domains() {
+  if (_is_security_manager_allowed) {
+    // For sanity, don't archive PDs. TODO: can this be relaxed?
+    return false;
+  }
+  // Archived PDs for the modules will reference their java.lang.Module, which must
+  // also be archived.
+  return ArchiveProtectionDomains && is_dumping_full_module_graph();
+}
+
+bool CDSConfig::is_loading_protection_domains() {
+  if (_is_security_manager_allowed) {
+    // For sanity, don't used any archived PDs. TODO: can this be relaxed?
+    return false;
+  }
+  return UseSharedSpaces && is_using_full_module_graph() && _is_loading_protection_domains;
+}
+
+bool CDSConfig::is_dumping_reflection_data() {
+  // reflection data use LambdaForm classes
+  return ArchiveReflectionData && is_dumping_invokedynamic();
+}
+
 bool CDSConfig::is_loading_invokedynamic() {
   return UseSharedSpaces && is_using_full_module_graph() && _has_archived_invokedynamic;
 }
 
 #endif // INCLUDE_CDS_JAVA_HEAP
+
+// This is allowed by default. We disable it only in the final image dump before the
+// metadata and heap are dumped.
+static bool _is_dumping_cached_code = true;
+
+bool CDSConfig::is_dumping_cached_code() {
+  return _is_dumping_cached_code;
+}
+
+void CDSConfig::disable_dumping_cached_code() {
+  _is_dumping_cached_code = false;
+}
+
+void CDSConfig::enable_dumping_cached_code() {
+  _is_dumping_cached_code = true;
+}
diff a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp
--- a/src/hotspot/share/cds/cdsConfig.hpp
+++ b/src/hotspot/share/cds/cdsConfig.hpp
@@ -27,10 +27,11 @@
 
 #include "memory/allStatic.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/macros.hpp"
 
+class InstanceKlass;
 class JavaThread;
 
 class CDSConfig : public AllStatic {
 #if INCLUDE_CDS
   static bool _is_dumping_static_archive;
@@ -38,10 +39,13 @@
   static bool _is_using_optimized_module_handling;
   static bool _is_dumping_full_module_graph;
   static bool _is_using_full_module_graph;
   static bool _has_aot_linked_classes;
   static bool _has_archived_invokedynamic;
+  static bool _is_loading_packages;
+  static bool _is_loading_protection_domains;
+  static bool _is_security_manager_allowed;
 
   static char* _default_archive_path;
   static char* _static_archive_path;
   static char* _dynamic_archive_path;
 
@@ -62,46 +66,58 @@
   // Used by jdk.internal.misc.CDS.getCDSConfigStatus();
   static const int IS_DUMPING_ARCHIVE              = 1 << 0;
   static const int IS_DUMPING_STATIC_ARCHIVE       = 1 << 1;
   static const int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 2;
   static const int IS_USING_ARCHIVE                = 1 << 3;
+  static const int IS_DUMPING_HEAP                 = 1 << 4;
+  static const int IS_LOGGING_DYNAMIC_PROXIES      = 1 << 5;
+  static const int IS_DUMPING_PACKAGES             = 1 << 6;
+  static const int IS_DUMPING_PROTECTION_DOMAINS   = 1 << 7;
   static int get_status() NOT_CDS_RETURN_(0);
 
   // Initialization and command-line checking
   static void initialize() NOT_CDS_RETURN;
   static void set_old_cds_flags_used()                       { CDS_ONLY(_old_cds_flags_used = true); }
   static bool old_cds_flags_used()                           { return CDS_ONLY(_old_cds_flags_used) NOT_CDS(false); }
   static void check_internal_module_property(const char* key, const char* value) NOT_CDS_RETURN;
   static void check_incompatible_property(const char* key, const char* value) NOT_CDS_RETURN;
   static void check_unsupported_dumping_module_options() NOT_CDS_RETURN;
   static bool has_unsupported_runtime_module_options() NOT_CDS_RETURN_(false);
-  static bool check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line) NOT_CDS_RETURN_(true);
+  static bool check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line, bool xshare_auto_cmd_line) NOT_CDS_RETURN_(true);
 
   // --- Basic CDS features
 
   // archive(s) in general
   static bool is_dumping_archive()                           { return is_dumping_static_archive() || is_dumping_dynamic_archive(); }
   static bool is_using_archive()                             NOT_CDS_RETURN_(false);
   static int num_archives(const char* archive_path)          NOT_CDS_RETURN_(0);
 
   // static_archive
-  static bool is_dumping_static_archive()                    { return CDS_ONLY(_is_dumping_static_archive) NOT_CDS(false); }
+  static bool is_dumping_static_archive()                    { return (CDS_ONLY(_is_dumping_static_archive) NOT_CDS(false))
+                                                                    || is_dumping_final_static_archive(); }
   static void enable_dumping_static_archive()                { CDS_ONLY(_is_dumping_static_archive = true); }
 
+  static bool is_dumping_classic_static_archive()                NOT_CDS_RETURN_(false); // -Xshare:dump
+  static bool is_dumping_preimage_static_archive()               NOT_CDS_RETURN_(false); // 1st phase of -XX:CacheDataStore dumping
+  static bool is_dumping_preimage_static_archive_with_triggers() NOT_CDS_RETURN_(false); // 1st phase of -XX:CacheDataStore dumping with triggers
+  static bool is_dumping_final_static_archive()                  NOT_CDS_RETURN_(false); // 2nd phase of -XX:CacheDataStore dumping
+
   // dynamic_archive
   static bool is_dumping_dynamic_archive()                   { return CDS_ONLY(_is_dumping_dynamic_archive) NOT_CDS(false); }
   static void enable_dumping_dynamic_archive()               { CDS_ONLY(_is_dumping_dynamic_archive = true); }
   static void disable_dumping_dynamic_archive()              { CDS_ONLY(_is_dumping_dynamic_archive = false); }
 
   // Misc CDS features
+  static bool preserve_all_dumptime_verification_states(const InstanceKlass* ik);
   static bool allow_only_single_java_thread()                NOT_CDS_RETURN_(false);
 
   // optimized_module_handling -- can we skip some expensive operations related to modules?
   static bool is_using_optimized_module_handling()           { return CDS_ONLY(_is_using_optimized_module_handling) NOT_CDS(false); }
   static void stop_using_optimized_module_handling()         NOT_CDS_RETURN;
 
   static bool is_logging_lambda_form_invokers()              NOT_CDS_RETURN_(false);
+  static bool is_dumping_regenerated_lambdaform_invokers()   NOT_CDS_RETURN_(false);
 
   static bool is_dumping_aot_linked_classes()                NOT_CDS_JAVA_HEAP_RETURN_(false);
   static bool is_using_aot_linked_classes()                  NOT_CDS_JAVA_HEAP_RETURN_(false);
   static void set_has_aot_linked_classes(bool has_aot_linked_classes) NOT_CDS_JAVA_HEAP_RETURN;
 
@@ -122,16 +138,34 @@
 
   static bool is_dumping_invokedynamic()                     NOT_CDS_JAVA_HEAP_RETURN_(false);
   static bool is_loading_invokedynamic()                     NOT_CDS_JAVA_HEAP_RETURN_(false);
   static void set_has_archived_invokedynamic()               { CDS_JAVA_HEAP_ONLY(_has_archived_invokedynamic = true); }
 
+  static bool is_dumping_packages()                          NOT_CDS_JAVA_HEAP_RETURN_(false);
+  static bool is_loading_packages()                          NOT_CDS_JAVA_HEAP_RETURN_(false);
+  static void set_is_loading_packages()                      { CDS_JAVA_HEAP_ONLY(_is_loading_packages = true); }
+
+  static bool is_dumping_protection_domains()                NOT_CDS_JAVA_HEAP_RETURN_(false);
+  static bool is_loading_protection_domains()                NOT_CDS_JAVA_HEAP_RETURN_(false);
+  static void set_is_loading_protection_domains()            { CDS_JAVA_HEAP_ONLY(_is_loading_protection_domains = true); }
+
+  static bool is_dumping_reflection_data()                   NOT_CDS_JAVA_HEAP_RETURN_(false);
+
+  static bool is_dumping_dynamic_proxies()                   NOT_CDS_JAVA_HEAP_RETURN_(false);
+  static bool is_logging_dynamic_proxies()                   NOT_CDS_RETURN_(false);
+
   // full_module_graph (requires optimized_module_handling)
   static bool is_dumping_full_module_graph()                 { return CDS_ONLY(_is_dumping_full_module_graph) NOT_CDS(false); }
   static bool is_using_full_module_graph()                   NOT_CDS_JAVA_HEAP_RETURN_(false);
   static void stop_dumping_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
   static void stop_using_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
 
+  // --- AOT compiler
+
+  static bool is_dumping_cached_code()                       NOT_CDS_RETURN_(false);
+  static void disable_dumping_cached_code()                  NOT_CDS_RETURN;
+  static void enable_dumping_cached_code()                   NOT_CDS_RETURN;
 
   // Some CDS functions assume that they are called only within a single-threaded context. I.e.,
   // they are called from:
   //    - The VM thread (e.g., inside VM_PopulateDumpSharedSpace)
   //    - The thread that performs prepatory steps before switching to the VM thread
diff a/src/hotspot/share/cds/cdsEndTrainingUpcall.cpp b/src/hotspot/share/cds/cdsEndTrainingUpcall.cpp
--- /dev/null
+++ b/src/hotspot/share/cds/cdsEndTrainingUpcall.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+
+
+
+#include "cds/cdsConfig.hpp"
+#include "cds/cdsEndTrainingUpcall.hpp"
+#include "cds/metaspaceShared.hpp"
+#include "compiler/methodMatcher.hpp"
+#include "runtime/globals_extension.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/runtimeUpcalls.hpp"
+
+uint volatile  CDSEndTrainingUpcall::_count = 0;
+uint           CDSEndTrainingUpcall::_limit = 1;
+int  volatile  CDSEndTrainingUpcall::_triggered = 0;
+BasicMatcher*  CDSEndTrainingUpcall::_matcher = nullptr;
+
+bool cdsEndTrainingUpcall_register_upcalls()
+{
+  if (!CDSConfig::is_dumping_preimage_static_archive_with_triggers()) {
+    return true;
+  }
+  return CDSEndTrainingUpcall::register_upcalls();
+}
+
+bool CDSEndTrainingUpcall::register_upcalls()
+{
+  if (!FLAG_IS_DEFAULT(AOTEndTrainingOnMethodEntry)) {
+    if (CDSEndTrainingUpcall::parse_vm_command(AOTEndTrainingOnMethodEntry)) {
+      return RuntimeUpcalls::register_upcall(
+            RuntimeUpcallType::onMethodEntry,
+            "end_training_check",
+            CDSEndTrainingUpcall::end_training_check,
+            CDSEndTrainingUpcall::filter_method_callback
+            );
+    }
+    return false;
+  }
+  return true;
+}
+
+JRT_ENTRY(void, CDSEndTrainingUpcall::end_training_check(JavaThread* current))
+{
+    if (_triggered == 0) {
+      Atomic::inc(&_count);
+      if(_count >= _limit) {
+        CDSEndTrainingUpcall::end_training(current);
+      }
+    }
+}
+JRT_END
+
+bool CDSEndTrainingUpcall::end_training(JavaThread* current)
+{
+  if (_triggered == 0) {
+    if (Atomic::cmpxchg(&_triggered, 0, 1) == 0) {
+      MetaspaceShared::preload_and_dump(current);
+      assert(!current->has_pending_exception(), "Unexpected exception");
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CDSEndTrainingUpcall::filter_method_callback(MethodDetails& method_details)
+{
+  if (_matcher != nullptr) {
+    return _matcher->match(method_details);
+  }
+  return false;
+}
+
+bool CDSEndTrainingUpcall::parse_vm_command(ccstrlist command)
+{
+  assert(command != nullptr, "sanity");
+  ResourceMark rm;
+  const char* error_msg = nullptr;
+  char* copy = os::strdup(command, mtInternal);
+  char* line = copy;
+  char* method_pattern;
+  int num_patterns = 0;
+  bool error = false;
+  const char* seperator_str = ",";
+  const char* count_str = "count=";
+  const size_t count_str_len = strlen(count_str);
+  do {
+    if (line[0] == '\0') {
+      break;
+    }
+    method_pattern = strtok_r(line, seperator_str, &line);
+    if (method_pattern != nullptr) {
+      // if method pattern starts with count=, then parse the count
+      if (strncmp(method_pattern, count_str, count_str_len) == 0) {
+        int number = atoi(method_pattern + count_str_len);
+        if (number > 0) {
+          CDSEndTrainingUpcall::set_limit((uint)number);
+          continue;
+        }
+        error_msg = "count must be a valid integer > 0";
+      } else {
+        BasicMatcher* matcher = BasicMatcher::parse_method_pattern(method_pattern, error_msg, false);
+        if (matcher != nullptr) {
+          if (_matcher != nullptr) {
+            matcher->set_next(_matcher);
+          }
+          _matcher = matcher;
+          num_patterns++;
+          continue;
+        }
+      }
+    }
+    ttyLocker ttyl;
+    tty->print_cr("An error occurred during parsing AOTEndTrainingOnMethodEntry");
+    if (error_msg != nullptr) {
+      tty->print_cr("Error: %s", error_msg);
+    }
+    tty->print_cr("Line: '%s'", command);
+    error = true;
+  } while (!error && method_pattern != nullptr && line != nullptr);
+  os::free(copy);
+  if (num_patterns == 0) {
+    tty->print_cr("Error: No method patterns found in AOTEndTrainingOnMethodEntry");
+    error = true;
+  }
+  return !error;
+}
diff a/src/hotspot/share/cds/cdsEndTrainingUpcall.hpp b/src/hotspot/share/cds/cdsEndTrainingUpcall.hpp
--- /dev/null
+++ b/src/hotspot/share/cds/cdsEndTrainingUpcall.hpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#ifndef SHARE_CDS_CDSENDTRAININGUPCALL_HPP
+#define SHARE_CDS_CDSENDTRAININGUPCALL_HPP
+
+#include "memory/allStatic.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "interpreter/linkResolver.hpp"
+#include "memory/allStatic.hpp"
+#include "memory/resourceArea.hpp"
+#include "utilities/macros.hpp"
+
+class BasicMatcher;
+class MethodDetails;
+class Symbol;
+
+class CDSEndTrainingUpcall : AllStatic {
+private:
+  static uint volatile  _count;
+  static uint           _limit;
+  static int  volatile  _triggered;
+  static BasicMatcher*  _matcher;
+
+  static bool parse_vm_command(ccstrlist command);
+  static void set_limit(uint limit) { _limit = limit; }
+
+public:
+  static bool register_upcalls();
+  static bool filter_method_callback(MethodDetails& method_details);
+  static void end_training_check(TRAPS);
+  static bool end_training(TRAPS);
+  static void set_method_entry_limit(uint limit) { _limit = limit; }
+};
+
+#endif // SHARE_CDS_CDSENDTRAININGUPCALL_HPP
diff a/src/hotspot/share/cds/cdsHeapVerifier.cpp b/src/hotspot/share/cds/cdsHeapVerifier.cpp
--- a/src/hotspot/share/cds/cdsHeapVerifier.cpp
+++ b/src/hotspot/share/cds/cdsHeapVerifier.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, 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.
@@ -94,11 +94,11 @@
   // Unfortunately this needs to be manually maintained. If
   // test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedEnumTest.java fails,
   // you might need to fix the core library code, or fix the ADD_EXCL entries below.
   //
   //       class                                         field                     type
-  ADD_EXCL("java/lang/ClassLoader",                      "scl");                   // A
+  ADD_EXCL("java/lang/ClassLoader$Holder",               "scl");                   // A
   ADD_EXCL("java/lang/Module",                           "ALL_UNNAMED_MODULE",     // A
                                                          "ALL_UNNAMED_MODULE_SET", // A
                                                          "EVERYONE_MODULE",        // A
                                                          "EVERYONE_SET");          // A
 
@@ -132,21 +132,27 @@
     ADD_EXCL("java/lang/invoke/InvokerBytecodeGenerator", "MEMBERNAME_FACTORY",    // D
                                                           "CD_Object_array",       // E same as <...>ConstantUtils.CD_Object_array::CD_Object
                                                           "INVOKER_SUPER_DESC");   // E same as java.lang.constant.ConstantDescs::CD_Object
   }
 
+  // These are used by BuiltinClassLoader::negativeLookupCache, etc but seem to be
+  // OK. TODO - we should completely disable the caching unless ArchiveLoaderLookupCache
+  // is enabled
+  ADD_EXCL("java/lang/Boolean",                           "TRUE",                  // E
+                                                          "FALSE");                // E
+
 # undef ADD_EXCL
 
   ClassLoaderDataGraph::classes_do(this);
 }
 
 CDSHeapVerifier::~CDSHeapVerifier() {
   if (_problems > 0) {
     log_error(cds, heap)("Scanned %d objects. Found %d case(s) where "
                          "an object points to a static field that "
                          "may hold a different value at runtime.", _archived_objs, _problems);
-    MetaspaceShared::unrecoverable_writing_error();
+    //MetaspaceShared::unrecoverable_writing_error(); // FIXME -- leyden+JEP483 merge
   }
 }
 
 class CDSHeapVerifier::CheckStaticFields : public FieldClosure {
   CDSHeapVerifier* _verifier;
diff a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp
--- a/src/hotspot/share/cds/cdsProtectionDomain.cpp
+++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp
@@ -58,10 +58,14 @@
     //   The ProtectionDomains are cached in the corresponding ModuleEntries
     //   for fast access by the VM.
     // all packages from module image are already created during VM bootstrap in
     // Modules::define_module().
     assert(pkg_entry != nullptr, "archived class in module image cannot be from unnamed package");
+    Handle archived_pd = get_archived_protection_domain(THREAD, ik);
+    if (archived_pd.not_null()) {
+      return archived_pd;
+    }
     ModuleEntry* mod_entry = pkg_entry->module();
     return get_shared_protection_domain(class_loader, mod_entry, THREAD);
   } else {
     // For shared app/platform classes originated from JAR files on the class path:
     //   Each of the 3 SystemDictionaryShared::_shared_xxx arrays has the same length
@@ -86,22 +90,28 @@
     //
     //   Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by
     //   the corresponding SystemDictionaryShared::get_shared_xxx() function.
     Handle manifest = get_shared_jar_manifest(index, CHECK_NH);
     Handle url = get_shared_jar_url(index, CHECK_NH);
-    int index_offset = index - ClassLoaderExt::app_class_paths_start_index();
-    if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) {
-      if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) {
-        // define_shared_package only needs to be called once for each package in a jar specified
-        // in the shared class path.
-        define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
-        if (pkg_entry != nullptr) {
-          pkg_entry->set_defined_by_cds_in_class_path(index_offset);
+    if (!CDSConfig::is_loading_packages()) {
+      int index_offset = index - ClassLoaderExt::app_class_paths_start_index();
+      if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) {
+        if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) {
+          // define_shared_package only needs to be called once for each package in a jar specified
+          // in the shared class path.
+          define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
+          if (pkg_entry != nullptr) {
+            pkg_entry->set_defined_by_cds_in_class_path(index_offset);
+          }
         }
+      } else {
+        define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
       }
-    } else {
-      define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
+    }
+    Handle archived_pd = get_archived_protection_domain(THREAD, ik);
+    if (archived_pd.not_null()) {
+      return archived_pd;
     }
     return get_shared_protection_domain(class_loader, index, url, THREAD);
   }
 }
 
@@ -207,10 +217,40 @@
   url_h = Handle(THREAD, shared_jar_url(shared_path_index));
   assert(url_h.not_null(), "sanity");
   return url_h;
 }
 
+
+Handle CDSProtectionDomain::get_archived_protection_domain(JavaThread* current, InstanceKlass* klass) {
+  oop pd = nullptr;
+
+  if (CDSConfig::is_loading_protection_domains()) {
+    oop mirror;
+    if (klass->has_archived_mirror_index()) {
+      mirror = klass->archived_java_mirror();
+    } else {
+      mirror = klass->java_mirror();
+    }
+
+    if (mirror != nullptr) {
+      pd = java_lang_Class::protection_domain(mirror);
+    }
+  }
+
+  if (log_is_enabled(Info, cds, protectiondomain)) {
+    ResourceMark rm;
+    log_info(cds, protectiondomain)("Archived protection domain for %s = %s", klass->external_name(),
+                                    (pd == nullptr) ? "none" : "found");
+  }
+
+  if (pd == nullptr) {
+    return Handle();
+  } else {
+    return Handle(current, pd);
+  }
+}
+
 oop CDSProtectionDomain::to_file_URL(const char* path, Handle url_h, TRAPS) {
   JavaValue result(T_OBJECT);
   Handle path_string = java_lang_String::create_from_str(path, CHECK_NULL);
   JavaCalls::call_static(&result,
                          vmClasses::jdk_internal_loader_ClassLoaders_klass(),
diff a/src/hotspot/share/cds/cdsProtectionDomain.hpp b/src/hotspot/share/cds/cdsProtectionDomain.hpp
--- a/src/hotspot/share/cds/cdsProtectionDomain.hpp
+++ b/src/hotspot/share/cds/cdsProtectionDomain.hpp
@@ -40,10 +40,11 @@
   // See init_security_info for more info.
   static OopHandle _shared_protection_domains;
   static OopHandle _shared_jar_urls;
   static OopHandle _shared_jar_manifests;
 
+  static Handle get_archived_protection_domain(JavaThread* current, InstanceKlass* klass);
 public:
   // Package handling:
   //
   // 1. For named modules in the runtime image
   //    BOOT classes: Reuses the existing JVM_GetSystemPackage(s) interfaces
diff a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp
--- a/src/hotspot/share/cds/cds_globals.hpp
+++ b/src/hotspot/share/cds/cds_globals.hpp
@@ -85,11 +85,12 @@
           "The path and name of the dynamic archive file")                  \
                                                                             \
   product(ccstr, ExtraSharedClassListFile, nullptr,                         \
           "Extra classlist for building the CDS archive file")              \
                                                                             \
-  product(int, ArchiveRelocationMode, 1, DIAGNOSTIC,                        \
+  /*FIXME - AOT code has direct pointers to metadata that's not relocated*/ \
+  product(int, ArchiveRelocationMode, 0, DIAGNOSTIC,                        \
            "(0) first map at preferred address, and if "                    \
            "unsuccessful, map at alternative address; "                     \
            "(1) always map at alternative address (default); "              \
            "(2) always map at preferred address, and if unsuccessful, "     \
            "do not map the archive")                                        \
@@ -115,10 +116,64 @@
           "ConstantPools")                                                  \
                                                                             \
   product(bool, AOTClassLinking, false,                                     \
           "Load/link all archived classes for the boot/platform/app "       \
           "loaders before application main")                                \
+                                                                            \
+  product(ccstrlist, AOTEndTrainingOnMethodEntry, "",                       \
+          "List of methods (pkg/class.name) to trigger end of AOT "         \
+          "training run.  Optional ',count=N' where N is > 0")              \
+                                                                            \
+  /*========== New options added by Leyden =============================*/  \
+                                                                            \
+  product(ccstr, CacheOnlyClassesIn, nullptr,                               \
+          "If set, only classes loaded from these JAR files will be "       \
+          "stored in the AOTCache")                                         \
+                                                                            \
+  product(ccstr, CacheDataStore, nullptr,                                   \
+          "If valid, use the specified file for SharedArchiveFile; "        \
+          "otherwise the specified file is generated at program exit")      \
+                                                                            \
+  product(ccstr, CDSPreimage, nullptr,                                      \
+          "(** internal use only **) -- used by a child JVM process to "    \
+          "create the CacheDataStore final image")                          \
+                                                                            \
+  product(bool, CDSManualFinalImage, false, DIAGNOSTIC,                     \
+          "(** internal use only **) -- if false, automatically launch a "  \
+          "child process to create the final image.")                       \
+                                                                            \
+  product(bool, PrelinkSharedClasses, true,                                 \
+          "Link all shared classes for the boot/platform/app loaders "      \
+          "immediately at VM start-up")                                     \
+                                                                            \
+  product(bool, ArchiveDynamicProxies, false,                               \
+          "Archive classes generated for java/lang/reflect/Proxy")          \
+                                                                            \
+  product(bool, ArchiveLoaderLookupCache, false,                            \
+          "Archive app loader's positive and negative lookup cache")        \
+                                                                            \
+  product(bool, ArchivePackages, false,                                     \
+          "Archive the java.lang.ClassLoader::{packages,package2certs} "    \
+          "tables")                                                         \
+                                                                            \
+  product(bool, ArchiveProtectionDomains, false,                            \
+          "Archive the java.security.SecureClassLoader::pdcache table")     \
+                                                                            \
+  product(bool, ArchiveReflectionData, false,                               \
+          "Archive Class::reflectionData field")                            \
+                                                                            \
+  product(bool, TempDisableAddJVMCIModule, false,                           \
+          "Do not add jdk.internal.vm.ci module even for -XX:+EnableJVMCI") \
+                                                                            \
+  product(bool, UsePermanentHeapObjects, false, DIAGNOSTIC,                 \
+          "Allow AOT code to access permanent archived heap objects")       \
+                                                                            \
+  product(bool, VerifyTrainingData, trueInDebug, DIAGNOSTIC,                \
+          "Verify archived training data")                                  \
+                                                                            \
+  product(bool, SkipArchiveHeapVerification, false,                         \
+          "Skip verification of CDS archive heap")                          \
 
 // end of CDS_FLAGS
 
 DECLARE_FLAGS(CDS_FLAGS)
 
diff a/src/hotspot/share/cds/classListParser.cpp b/src/hotspot/share/cds/classListParser.cpp
--- a/src/hotspot/share/cds/classListParser.cpp
+++ b/src/hotspot/share/cds/classListParser.cpp
@@ -23,42 +23,49 @@
  */
 
 #include "precompiled.hpp"
 #include "cds/aotConstantPoolResolver.hpp"
 #include "cds/archiveUtils.hpp"
+#include "cds/cdsConfig.hpp"
 #include "cds/classListParser.hpp"
 #include "cds/lambdaFormInvokers.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "cds/unregisteredClasses.hpp"
-#include "classfile/classLoaderExt.hpp"
+#include "classfile/classLoader.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "interpreter/bytecode.hpp"
-#include "interpreter/bytecodeStream.hpp"
+#include "interpreter/interpreterRuntime.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "jimage.hpp"
 #include "jvm.h"
 #include "logging/log.hpp"
 #include "logging/logTag.hpp"
+#include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/constantPool.inline.hpp"
+#include "oops/cpCache.inline.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/globals_extension.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/utf8.hpp"
 
+const char* ClassListParser::CLASS_REFLECTION_DATA_TAG = "@class-reflection-data";
 const char* ClassListParser::CONSTANT_POOL_TAG = "@cp";
+const char* ClassListParser::DYNAMIC_PROXY_TAG = "@dynamic-proxy";
 const char* ClassListParser::LAMBDA_FORM_TAG = "@lambda-form-invoker";
 const char* ClassListParser::LAMBDA_PROXY_TAG = "@lambda-proxy";
+const char* ClassListParser::LOADER_NEGATIVE_CACHE_TAG = "@loader-negative-cache";
+const char* ClassListParser::ARRAY_TAG = "@array";
 
 volatile Thread* ClassListParser::_parsing_thread = nullptr;
 ClassListParser* ClassListParser::_instance = nullptr;
 
 ClassListParser::ClassListParser(const char* file, ParseMode parse_mode) :
@@ -310,10 +317,22 @@
   } else if (strcmp(_token, LAMBDA_FORM_TAG) == 0) {
     LambdaFormInvokers::append(os::strdup((const char*)(_line + offset), mtInternal));
   } else if (strcmp(_token, CONSTANT_POOL_TAG) == 0) {
     _token = _line + offset;
     parse_constant_pool_tag();
+  } else if (strcmp(_token, ARRAY_TAG) == 0) {
+    _token = _line + offset;
+    parse_array_dimension_tag();
+  } else if (strcmp(_token, CLASS_REFLECTION_DATA_TAG) == 0) {
+    _token = _line + offset;
+    parse_class_reflection_data_tag();
+  } else if (strcmp(_token, DYNAMIC_PROXY_TAG) == 0) {
+    _token = _line + offset;
+    parse_dynamic_proxy_tag();
+  } else if (strcmp(_token, LOADER_NEGATIVE_CACHE_TAG) == 0) {
+    _token = _line + offset;
+    parse_loader_negative_cache_tag();
   } else {
     error("Invalid @ tag at the beginning of line \"%s\" line #%zu", _token, lineno());
   }
 }
 
@@ -796,10 +815,46 @@
   } else {
     return nullptr;
   }
 }
 
+void ClassListParser::parse_array_dimension_tag() {
+  if (parse_lambda_forms_invokers_only()) {
+    return;
+  }
+
+  skip_whitespaces();
+  char* class_name = _token;
+  skip_non_whitespaces();
+  *_token = '\0';
+  _token ++;
+
+  skip_whitespaces();
+  int dim;
+  parse_uint(&dim);
+
+  JavaThread* THREAD = JavaThread::current();
+  InstanceKlass* ik = find_builtin_class(THREAD, class_name);
+  if (ik == nullptr) {
+    _token = class_name;
+    if (strstr(class_name, "/$Proxy") != nullptr ||
+        strstr(class_name, "MethodHandle$Species_") != nullptr) {
+      // ignore -- TODO: we should filter these out in classListWriter.cpp
+    } else {
+      constant_pool_resolution_warning("class %s is not (yet) loaded by one of the built-in loaders", class_name);
+    }
+    return;
+  }
+
+  if (dim > 0) {
+    ik->array_klass(dim, THREAD);
+    if (HAS_PENDING_EXCEPTION) {
+      error("Array klass allocation failed: %s %d", _class_name, dim);
+    }
+  }
+}
+
 void ClassListParser::parse_constant_pool_tag() {
   if (parse_lambda_forms_invokers_only()) {
     return;
   }
 
@@ -865,11 +920,183 @@
 
   if (preresolve_class) {
     AOTConstantPoolResolver::preresolve_class_cp_entries(THREAD, ik, &preresolve_list);
   }
   if (preresolve_fmi) {
+// FIXME: too coarse; doesn't cover resolution of Class entries
+//    JavaThread::NoJavaCodeMark no_java_code(THREAD); // ensure no clinits are exectued
     AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(THREAD, ik, &preresolve_list);
   }
   if (preresolve_indy) {
     AOTConstantPoolResolver::preresolve_indy_cp_entries(THREAD, ik, &preresolve_list);
   }
 }
+
+void ClassListParser::parse_class_reflection_data_tag() {
+  if (parse_lambda_forms_invokers_only()) {
+    return;
+  }
+
+  JavaThread* THREAD = JavaThread::current();
+  skip_whitespaces();
+  char* class_name = _token;
+  skip_non_whitespaces();
+  *_token = '\0';
+  _token ++;
+
+  InstanceKlass* ik = find_builtin_class(THREAD, class_name);
+  if (ik == nullptr) {
+    _token = class_name;
+    if (strstr(class_name, "/$Proxy") != nullptr ||
+        strstr(class_name, "MethodHandle$Species_") != nullptr) {
+      // ignore -- TODO: we should filter these out in classListWriter.cpp
+    } else {
+      warning("%s: class not found: %s", CLASS_REFLECTION_DATA_TAG, class_name);
+    }
+    return;
+  }
+
+  ResourceMark rm(THREAD);
+
+  int rd_flags = _unspecified;
+  while (*_token) {
+    skip_whitespaces();
+    if (rd_flags != _unspecified) {
+      error("rd_flags specified twice");
+      return;
+    }
+    parse_uint(&rd_flags);
+  }
+  if (rd_flags == _unspecified) {
+    error("no rd_flags specified");
+    return;
+  }
+
+  if (CDSConfig::is_dumping_reflection_data()) {
+    AOTConstantPoolResolver::generate_reflection_data(THREAD, ik, rd_flags);
+  }
+}
+
+oop ClassListParser::loader_from_type(const char* loader_type) {
+  oop loader;
+  if (!ArchiveUtils::builtin_loader_from_type(loader_type, &loader)) {
+    error("Unknown loader %s", loader_type);
+  }
+  return loader;
+}
+
+void ClassListParser::parse_dynamic_proxy_tag() {
+  if (parse_lambda_forms_invokers_only()) {
+    return;
+  }
+
+  skip_whitespaces();
+  char* loader_type = _token;
+  skip_non_whitespaces();
+  *_token = '\0';
+  _token ++;
+
+  skip_whitespaces();
+  char* proxy_name_str = _token;
+  skip_non_whitespaces();
+  *_token = '\0';
+  _token ++;
+
+  skip_whitespaces();
+  int access_flags;
+  parse_uint(&access_flags);
+
+  skip_whitespaces();
+  int num_intfs;
+  parse_uint(&num_intfs);
+
+  JavaThread* THREAD = JavaThread::current();
+  Handle loader(THREAD, loader_from_type(loader_type));
+  Handle proxy_name(THREAD, java_lang_String::create_oop_from_str(proxy_name_str, THREAD));
+  if (HAS_PENDING_EXCEPTION) {
+    error("Out of memory");
+  }
+
+  objArrayHandle interfaces(THREAD, oopFactory::new_objArray(vmClasses::Class_klass(), num_intfs, THREAD));
+  if (HAS_PENDING_EXCEPTION) {
+    error("Out of memory");
+  }
+
+  for (int i = 0; i < num_intfs; i++) {
+    skip_whitespaces();
+    char* intf_name = _token;
+    skip_non_whitespaces();
+    *_token = '\0';
+    _token ++;
+
+    InstanceKlass* ik = find_builtin_class(THREAD, intf_name);
+    if (ik != nullptr) {
+      interfaces()->obj_at_put(i, ik->java_mirror());
+    } else {
+      error("Unknown class %s", intf_name);
+    }
+  }
+
+  if (strncmp("jdk.proxy", proxy_name_str, 9) != 0) {
+    return;
+  }
+
+  AOTConstantPoolResolver::define_dynamic_proxy_class(loader, proxy_name, interfaces, access_flags, THREAD);
+  if (HAS_PENDING_EXCEPTION) {
+    PENDING_EXCEPTION->print_on(tty);
+    error("defineProxyClassForCDS failed");
+  }
+}
+
+void ClassListParser::parse_loader_negative_cache_tag() {
+  skip_whitespaces();
+  char* loader_type = _token;
+  skip_non_whitespaces();
+  *_token = '\0';
+  _token ++;
+
+  oop loader;
+  Klass* loader_klass;
+  if (!strcmp(loader_type, "app")) {
+    loader = SystemDictionary::java_system_loader();
+    loader_klass = vmClasses::jdk_internal_loader_ClassLoaders_AppClassLoader_klass();
+  } else if (!strcmp(loader_type, "platform")) {
+    loader = SystemDictionary::java_platform_loader();
+    loader_klass = vmClasses::jdk_internal_loader_ClassLoaders_PlatformClassLoader_klass();
+  } else {
+    warning("%s: unrecognized loader type %s is ignored", LOADER_NEGATIVE_CACHE_TAG, loader_type);
+    return;
+  }
+
+  char* contents = _token;
+  skip_non_whitespaces();
+  *_token = '\0';
+  _token ++;
+
+  if (ArchiveLoaderLookupCache) {
+    TempNewSymbol method = SymbolTable::new_symbol("generateNegativeLookupCache");
+    TempNewSymbol signature = SymbolTable::new_symbol("(Ljava/lang/String;)V");
+
+    EXCEPTION_MARK;
+    HandleMark hm(THREAD);
+    JavaCallArguments args(Handle(THREAD, loader));
+    Handle contents_h = java_lang_String::create_from_str(contents, THREAD);
+    args.push_oop(contents_h);
+    JavaValue result(T_VOID);
+    JavaCalls::call_virtual(&result,
+                            loader_klass,
+                            method,
+                            signature,
+                            &args, THREAD);
+    if (HAS_PENDING_EXCEPTION) {
+      Handle exc_handle(THREAD, PENDING_EXCEPTION);
+      CLEAR_PENDING_EXCEPTION;
+
+      log_warning(cds)("Exception during BuiltinClassLoader::generateNegativeLookupCache() call for %s loader", loader_type);
+      LogStreamHandle(Debug, cds) log;
+      if (log.is_enabled()) {
+        java_lang_Throwable::print_stack_trace(exc_handle, &log);
+      }
+    }
+  }
+}
+
diff a/src/hotspot/share/cds/classListParser.hpp b/src/hotspot/share/cds/classListParser.hpp
--- a/src/hotspot/share/cds/classListParser.hpp
+++ b/src/hotspot/share/cds/classListParser.hpp
@@ -23,10 +23,11 @@
  */
 
 #ifndef SHARE_CDS_CLASSLISTPARSER_HPP
 #define SHARE_CDS_CLASSLISTPARSER_HPP
 
+#include "interpreter/bytecodes.hpp"
 #include "utilities/exceptions.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/istream.hpp"
 #include "utilities/resizeableResourceHash.hpp"
@@ -64,14 +65,20 @@
   }
 };
 
 class ClassListParser : public StackObj {
   static const char* CONSTANT_POOL_TAG;
+  static const char* DYNAMIC_PROXY_TAG;
   static const char* LAMBDA_FORM_TAG;
   static const char* LAMBDA_PROXY_TAG;
 
 public:
+  static const char* ARRAY_TAG;
+
+  static const char* CLASS_REFLECTION_DATA_TAG;
+  static const char* LOADER_NEGATIVE_CACHE_TAG;
+
   enum ParseMode {
     _parse_all,
     _parse_lambda_forms_invokers_only,
   };
 
@@ -126,19 +133,24 @@
   void clean_up_input_line();
   void read_class_name_and_attributes();
   void parse_class_name_and_attributes(TRAPS);
   Klass* load_current_class(Symbol* class_name_symbol, TRAPS);
   void parse_constant_pool_tag();
+  void parse_class_reflection_data_tag();
+  void parse_dynamic_proxy_tag();
+  void parse_loader_negative_cache_tag();
+  void parse_array_dimension_tag();
 
   size_t lineno() { return _input_stream.lineno(); }
   FILE* do_open(const char* file);
   ClassListParser(const char* file, ParseMode _parse_mode);
   ~ClassListParser();
   void print_diagnostic_info(outputStream* st, const char* msg, va_list ap) ATTRIBUTE_PRINTF(3, 0);
   void print_diagnostic_info(outputStream* st, const char* msg, ...) ATTRIBUTE_PRINTF(3, 0);
   void constant_pool_resolution_warning(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
   void error(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
+  oop loader_from_type(const char* loader_name);
 
 public:
   static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
     ClassListParser parser(classlist_path, parse_mode);
     parser.parse(THREAD);
diff a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp
--- a/src/hotspot/share/cds/classListWriter.cpp
+++ b/src/hotspot/share/cds/classListWriter.cpp
@@ -21,22 +21,26 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotConstantPoolResolver.hpp"
 #include "cds/cds_globals.hpp"
+#include "cds/classListParser.hpp"
 #include "cds/classListWriter.hpp"
 #include "cds/lambdaFormInvokers.inline.hpp"
 #include "classfile/classFileStream.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderData.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/moduleEntry.hpp"
+#include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/instanceKlass.hpp"
+#include "runtime/javaCalls.hpp"
 #include "runtime/mutexLocker.hpp"
 
 fileStream* ClassListWriter::_classlist_file = nullptr;
 
 void ClassListWriter::init() {
@@ -206,15 +210,28 @@
   void do_cld(ClassLoaderData* cld) {
     for (Klass* klass = cld->klasses(); klass != nullptr; klass = klass->next_link()) {
       if (klass->is_instance_klass()) {
         InstanceKlass* ik = InstanceKlass::cast(klass);
         write_resolved_constants_for(ik);
+        write_array_info_for(ik); // FIXME: piggybacking on WriteResolveConstantsCLDClosure is misleading
       }
     }
   }
 };
 
+void ClassListWriter::write_array_info_for(InstanceKlass* ik) {
+  ObjArrayKlass* oak = ik->array_klasses();
+  if (oak != nullptr) {
+    while (oak->higher_dimension() != nullptr) {
+      oak = oak->higher_dimension();
+    }
+    ResourceMark rm;
+    outputStream* stream = _classlist_file;
+    stream->print_cr("%s %s %d", ClassListParser::ARRAY_TAG, ik->name()->as_C_string(), oak->dimension());
+  }
+}
+
 void ClassListWriter::write_resolved_constants() {
   if (!is_enabled()) {
     return;
   }
   MutexLocker lock(ClassLoaderDataGraph_lock);
@@ -222,10 +239,37 @@
 
   WriteResolveConstantsCLDClosure closure;
   ClassLoaderDataGraph::loaded_cld_do(&closure);
 }
 
+void ClassListWriter::write_reflection_data() {
+  if (!is_enabled()) {
+    return;
+  }
+  auto collector = [&] (const InstanceKlass* ik, int id) {
+    write_reflection_data_for(const_cast<InstanceKlass*>(ik));
+  };
+  _id_table->iterate_all(collector);
+}
+
+void ClassListWriter::write_reflection_data_for(InstanceKlass* ik) {
+  ResourceMark rm;
+  outputStream* stream = _classlist_file;
+  if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data()) || ik->is_hidden()) {
+    return; // ignore
+  }
+  if (java_lang_Class::has_reflection_data(ik->java_mirror())) {
+    EXCEPTION_MARK;
+    int rd_flags = AOTConstantPoolResolver::class_reflection_data_flags(ik, THREAD);
+    if (!HAS_PENDING_EXCEPTION) {
+      // We can't hold the lock when doing the upcall inside class_reflection_data_flags()
+      MutexLocker lock2(ClassListFile_lock, Mutex::_no_safepoint_check_flag);
+      stream->print_cr("%s %s %d", ClassListParser::CLASS_REFLECTION_DATA_TAG, ik->name()->as_C_string(), rd_flags);
+    }
+  }
+}
+
 void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) {
   if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data()) ||
       ik->is_hidden()) {
     return;
   }
@@ -291,10 +335,11 @@
       for (int i = 0; i < method_entries->length(); i++) {
         ResolvedMethodEntry* rme = method_entries->adr_at(i);
         if (rme->is_resolved(Bytecodes::_invokevirtual) ||
             rme->is_resolved(Bytecodes::_invokespecial) ||
             rme->is_resolved(Bytecodes::_invokeinterface) ||
+            rme->is_resolved(Bytecodes::_invokestatic) ||
             rme->is_resolved(Bytecodes::_invokehandle)) {
           list.at_put(rme->constant_pool_index(), true);
           print = true;
         }
       }
@@ -316,5 +361,52 @@
       }
     }
     stream->cr();
   }
 }
+
+void ClassListWriter::write_loader_negative_lookup_cache_for(oop loader, const char* loader_type) {
+  TempNewSymbol method = SymbolTable::new_symbol("negativeLookupCacheContents");
+  TempNewSymbol signature = SymbolTable::new_symbol("()Ljava/lang/String;");
+
+  EXCEPTION_MARK;
+  HandleMark hm(THREAD);
+
+  JavaValue result(T_OBJECT);
+  JavaCalls::call_virtual(&result,
+                          Handle(THREAD, loader),
+                          loader->klass(),
+                          method,
+                          signature,
+                          CHECK);
+
+  if (HAS_PENDING_EXCEPTION) {
+    log_warning(cds)("Error during BuiltinClassLoader::negativeLookupCacheContents() call for %s loader", loader_type);
+    CLEAR_PENDING_EXCEPTION;
+    return;
+  } else if (result.get_oop() == nullptr) {
+    return;
+  }
+
+  ResourceMark rm;
+  const char* cache_contents = java_lang_String::as_utf8_string(result.get_oop());
+  log_debug(cds)("%s loader negative cache: %s", loader_type, cache_contents);
+
+  outputStream* stream = _classlist_file;
+  const size_t buffer_size = strlen(ClassListParser::LOADER_NEGATIVE_CACHE_TAG) + 1 /* for space*/
+                    + strlen(loader_type) + 1 /* for space */ + strlen(cache_contents) + 1 /* for null character */;
+  char* buffer = NEW_C_HEAP_ARRAY(char, buffer_size, mtInternal);
+  _classlist_file->set_scratch_buffer(buffer, buffer_size);
+  MutexLocker lock2(ClassListFile_lock, Mutex::_no_safepoint_check_flag);
+  stream->print("%s %s %s", ClassListParser::LOADER_NEGATIVE_CACHE_TAG, loader_type, cache_contents);
+  stream->cr();
+  _classlist_file->set_scratch_buffer(nullptr, 0);
+}
+
+void ClassListWriter::write_loader_negative_lookup_cache() {
+  if (!is_enabled()) {
+    return;
+  }
+
+  write_loader_negative_lookup_cache_for(SystemDictionary::java_platform_loader(), "platform");
+  write_loader_negative_lookup_cache_for(SystemDictionary::java_system_loader(), "app");
+}
diff a/src/hotspot/share/cds/classListWriter.hpp b/src/hotspot/share/cds/classListWriter.hpp
--- a/src/hotspot/share/cds/classListWriter.hpp
+++ b/src/hotspot/share/cds/classListWriter.hpp
@@ -43,10 +43,13 @@
 
   static int get_id(const InstanceKlass* k);
   static bool has_id(const InstanceKlass* k);
   static void assert_locked() { assert_lock_strong(ClassListFile_lock); }
   static void write_resolved_constants_for(InstanceKlass* klass);
+  static void write_array_info_for(InstanceKlass* klass);
+  static void write_reflection_data_for(InstanceKlass* klass);
+  static void write_loader_negative_lookup_cache_for(oop loader, const char* loader_type);
 public:
   ClassListWriter() : _locker(Thread::current(), ClassListFile_lock, Mutex::_no_safepoint_check_flag) {}
 
   outputStream* stream() {
     return _classlist_file;
@@ -68,9 +71,11 @@
 
   static void init() NOT_CDS_RETURN;
   static void write(const InstanceKlass* k, const ClassFileStream* cfs) NOT_CDS_RETURN;
   static void write_to_stream(const InstanceKlass* k, outputStream* stream, const ClassFileStream* cfs = nullptr) NOT_CDS_RETURN;
   static void write_resolved_constants() NOT_CDS_RETURN;
+  static void write_reflection_data() NOT_CDS_RETURN;
+  static void write_loader_negative_lookup_cache() NOT_CDS_RETURN;
   static void delete_classlist() NOT_CDS_RETURN;
 };
 
 #endif // SHARE_CDS_CLASSLISTWRITER_HPP
diff a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp
--- a/src/hotspot/share/cds/cppVtables.cpp
+++ b/src/hotspot/share/cds/cppVtables.cpp
@@ -31,11 +31,13 @@
 #include "logging/log.hpp"
 #include "oops/instanceClassLoaderKlass.hpp"
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/instanceRefKlass.hpp"
 #include "oops/instanceStackChunkKlass.hpp"
+#include "oops/methodCounters.hpp"
 #include "oops/methodData.hpp"
+#include "oops/trainingData.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/typeArrayKlass.hpp"
 #include "runtime/arguments.hpp"
 #include "utilities/globalDefinitions.hpp"
 
@@ -59,12 +61,17 @@
   f(InstanceClassLoaderKlass) \
   f(InstanceMirrorKlass) \
   f(InstanceRefKlass) \
   f(InstanceStackChunkKlass) \
   f(Method) \
+  f(MethodData)                \
+  f(MethodCounters)            \
   f(ObjArrayKlass) \
-  f(TypeArrayKlass)
+  f(TypeArrayKlass)            \
+  f(KlassTrainingData)         \
+  f(MethodTrainingData)        \
+  f(CompileTrainingData)
 
 class CppVtableInfo {
   intptr_t _vtable_size;
   intptr_t _cloned_vtable[1]; // Pseudo flexible array member.
   static size_t cloned_vtable_offset() { return offset_of(CppVtableInfo, _cloned_vtable); }
@@ -192,11 +199,12 @@
 
 // This is a map of all the original vtptrs. E.g., for
 //     ConstantPool *cp = new (...) ConstantPool(...) ; // a dynamically allocated constant pool
 // the following holds true:
 //     _orig_cpp_vtptrs[ConstantPool_Kind] ==  ((intptr_t**)cp)[0]
-static intptr_t* _orig_cpp_vtptrs[_num_cloned_vtable_kinds];
+static intptr_t* _orig_cpp_vtptrs[_num_cloned_vtable_kinds];  // vtptrs set by the C++ compiler
+static intptr_t* _archived_cpp_vtptrs[_num_cloned_vtable_kinds];  // vtptrs used in the static archive
 static bool _orig_cpp_vtptrs_inited = false;
 
 template <class T>
 void CppVtableCloner<T>::init_orig_cpp_vtptr(int kind) {
   assert(kind < _num_cloned_vtable_kinds, "sanity");
@@ -222,10 +230,16 @@
 void CppVtables::dumptime_init(ArchiveBuilder* builder) {
   assert(CDSConfig::is_dumping_static_archive(), "cpp tables are only dumped into static archive");
 
   CPP_VTABLE_TYPES_DO(ALLOCATE_AND_INITIALIZE_VTABLE);
 
+  if (!CDSConfig::is_dumping_final_static_archive()) {
+    for (int kind = 0; kind < _num_cloned_vtable_kinds; kind++) {
+      _archived_cpp_vtptrs[kind] = _index[kind]->cloned_vtable();
+    }
+  }
+
   size_t cpp_tables_size = builder->rw_region()->top() - builder->rw_region()->base();
   builder->alloc_stats()->record_cpp_vtables((int)cpp_tables_size);
 }
 
 void CppVtables::serialize(SerializeClosure* soc) {
@@ -236,10 +250,18 @@
     soc->do_ptr(&_index[i]);
   }
   if (soc->reading()) {
     CPP_VTABLE_TYPES_DO(INITIALIZE_VTABLE);
   }
+
+  if (soc->writing() && CDSConfig::is_dumping_final_static_archive()) {
+    memset(_archived_cpp_vtptrs, 0, sizeof(_archived_cpp_vtptrs));
+  }
+
+  for (int kind = 0; kind < _num_cloned_vtable_kinds; kind++) {
+    soc->do_ptr(&_archived_cpp_vtptrs[kind]);
+  }
 }
 
 intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address obj) {
   if (!_orig_cpp_vtptrs_inited) {
     CPP_VTABLE_TYPES_DO(INIT_ORIG_CPP_VTPTRS);
@@ -256,22 +278,18 @@
   case MetaspaceObj::TypeArrayU8Type:
   case MetaspaceObj::TypeArrayOtherType:
   case MetaspaceObj::ConstMethodType:
   case MetaspaceObj::ConstantPoolCacheType:
   case MetaspaceObj::AnnotationsType:
-  case MetaspaceObj::MethodCountersType:
   case MetaspaceObj::SharedClassPathEntryType:
   case MetaspaceObj::RecordComponentType:
     // These have no vtables.
     break;
-  case MetaspaceObj::MethodDataType:
-    // We don't archive MethodData <-- should have been removed in removed_unsharable_info
-    ShouldNotReachHere();
-    break;
   default:
     for (kind = 0; kind < _num_cloned_vtable_kinds; kind ++) {
-      if (vtable_of((Metadata*)obj) == _orig_cpp_vtptrs[kind]) {
+      if (vtable_of((Metadata*)obj) == _orig_cpp_vtptrs[kind] ||
+          vtable_of((Metadata*)obj) == _archived_cpp_vtptrs[kind]) {
         break;
       }
     }
     if (kind >= _num_cloned_vtable_kinds) {
       fatal("Cannot find C++ vtable for " INTPTR_FORMAT " -- you probably added"
diff a/src/hotspot/share/cds/dumpAllocStats.cpp b/src/hotspot/share/cds/dumpAllocStats.cpp
--- a/src/hotspot/share/cds/dumpAllocStats.cpp
+++ b/src/hotspot/share/cds/dumpAllocStats.cpp
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "cds/aotClassLinker.hpp"
+#include "cds/cdsConfig.hpp"
 #include "cds/dumpAllocStats.hpp"
 #include "logging/log.hpp"
 #include "logging/logMessage.hpp"
 
 void DumpAllocStats::print_stats(int ro_all, int rw_all) {
@@ -119,10 +120,12 @@
            _num_indy_cp_entries, _num_indy_cp_entries_archived,
            percent_of(_num_indy_cp_entries_archived, _num_indy_cp_entries),
            _num_indy_cp_entries_reverted);
   msg.info("Platform loader initiated classes = %5d", AOTClassLinker::num_platform_initiated_classes());
   msg.info("App      loader initiated classes = %5d", AOTClassLinker::num_app_initiated_classes());
+  msg.info("Dynamic proxy classes             = %5d%s", _num_dynamic_proxy_classes,
+           CDSConfig::is_dumping_full_module_graph() ? "" : " (not archiving FMG)");
 }
 
 #ifdef ASSERT
 void DumpAllocStats::verify(int expected_byte_size, bool read_only) const {
   int bytes = 0;
diff a/src/hotspot/share/cds/dumpAllocStats.hpp b/src/hotspot/share/cds/dumpAllocStats.hpp
--- a/src/hotspot/share/cds/dumpAllocStats.hpp
+++ b/src/hotspot/share/cds/dumpAllocStats.hpp
@@ -75,10 +75,11 @@
   int _num_klass_cp_entries_archived;
   int _num_klass_cp_entries_reverted;
   int _num_method_cp_entries;
   int _num_method_cp_entries_archived;
   int _num_method_cp_entries_reverted;
+  int _num_dynamic_proxy_classes;
 
 public:
   enum { RO = 0, RW = 1 };
 
   DumpAllocStats() {
@@ -94,10 +95,11 @@
     _num_klass_cp_entries_archived  = 0;
     _num_klass_cp_entries_reverted  = 0;
     _num_method_cp_entries          = 0;
     _num_method_cp_entries_archived = 0;
     _num_method_cp_entries_reverted = 0;
+    _num_dynamic_proxy_classes      = 0;
   };
 
   CompactHashtableStats* symbol_stats() { return &_symbol_stats; }
   CompactHashtableStats* string_stats() { return &_string_stats; }
 
@@ -144,10 +146,14 @@
     _num_method_cp_entries ++;
     _num_method_cp_entries_archived += archived ? 1 : 0;
     _num_method_cp_entries_reverted += reverted ? 1 : 0;
   }
 
+  void record_dynamic_proxy_class() {
+    _num_dynamic_proxy_classes ++;
+  }
+
   void print_stats(int ro_all, int rw_all);
 
   DEBUG_ONLY(void verify(int expected_byte_size, bool read_only) const);
 
 };
diff a/src/hotspot/share/cds/dumpTimeClassInfo.cpp b/src/hotspot/share/cds/dumpTimeClassInfo.cpp
--- a/src/hotspot/share/cds/dumpTimeClassInfo.cpp
+++ b/src/hotspot/share/cds/dumpTimeClassInfo.cpp
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "cds/archiveBuilder.hpp"
+#include "cds/cdsConfig.hpp"
 #include "cds/dumpTimeClassInfo.inline.hpp"
 #include "cds/runTimeClassInfo.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderData.inline.hpp"
 #include "classfile/systemDictionaryShared.hpp"
@@ -40,10 +41,34 @@
   if (_loader_constraints != nullptr) {
     delete _loader_constraints;
   }
 }
 
+bool DumpTimeClassInfo::is_excluded() {
+  if (_excluded) {
+    return true;
+  }
+  if (_failed_verification) {
+    if (CDSConfig::preserve_all_dumptime_verification_states(_klass)) {
+      assert(CDSConfig::is_dumping_aot_linked_classes(), "sanity");
+      // If the verification states are preserved, _klass will be archived in unlinked state. This is
+      // necessary to support the following scenario, where the verification of X requires that
+      // A be a subclass of B:
+      //     class X {
+      //        B getB() { return new A(); }
+      //     }
+      // If X and B can be verified, but A fails verification, we still archive A (in a preloaded
+      // SystemDictionary) so that at runtime we cannot subvert the verification of X by replacing
+      // A with a version that is not a subtype of B.
+    } else {
+      // Don't archive this class. At runtime, load it from classfile and rerun verification.
+      return true;
+    }
+  }
+  return false;
+}
+
 size_t DumpTimeClassInfo::runtime_info_bytesize() const {
   return RunTimeClassInfo::byte_size(_klass, num_verifier_constraints(),
                                      num_loader_constraints(),
                                      num_enum_klass_static_fields());
 }
@@ -141,20 +166,24 @@
 bool DumpTimeClassInfo::is_builtin() {
   return SystemDictionaryShared::is_builtin(_klass);
 }
 
 DumpTimeClassInfo* DumpTimeSharedClassTable::allocate_info(InstanceKlass* k) {
-  assert(!k->is_shared(), "Do not call with shared classes");
+  if (!CDSConfig::is_dumping_final_static_archive()) {
+    assert(!k->is_shared(), "Do not call with shared classes");
+  }
   bool created;
   DumpTimeClassInfo* p = put_if_absent(k, &created);
   assert(created, "must not exist in table");
   p->_klass = k;
   return p;
 }
 
 DumpTimeClassInfo* DumpTimeSharedClassTable::get_info(InstanceKlass* k) {
-  assert(!k->is_shared(), "Do not call with shared classes");
+  if (!CDSConfig::is_dumping_final_static_archive()) {
+    assert(!k->is_shared(), "Do not call with shared classes");
+  }
   DumpTimeClassInfo* p = get(k);
   assert(p != nullptr, "we must not see any non-shared InstanceKlass* that's "
          "not stored with SystemDictionaryShared::init_dumptime_info");
   assert(p->_klass == k, "Sanity");
   return p;
diff a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp
--- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp
+++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp
@@ -40,10 +40,12 @@
 
 class DumpTimeClassInfo: public CHeapObj<mtClass> {
   bool                         _excluded;
   bool                         _is_early_klass;
   bool                         _has_checked_exclusion;
+  bool                         _can_be_preinited;
+  bool                         _has_done_preinit_check;
   bool                         _is_required_hidden_class;
   bool                         _has_scanned_constant_pool;
   class DTLoaderConstraint {
     Symbol* _name;
     char _loader_type1;
@@ -136,10 +138,12 @@
     _klass = nullptr;
     _nest_host = nullptr;
     _failed_verification = false;
     _is_archived_lambda_proxy = false;
     _has_checked_exclusion = false;
+    _can_be_preinited = false;
+    _has_done_preinit_check = false;
     _is_required_hidden_class = false;
     _has_scanned_constant_pool = false;
     _id = -1;
     _clsfile_size = -1;
     _clsfile_crc32 = -1;
@@ -197,13 +201,11 @@
         _loader_constraints->adr_at(i)->metaspace_pointers_do(it);
       }
     }
   }
 
-  bool is_excluded() {
-    return _excluded || _failed_verification;
-  }
+  bool is_excluded();
 
   // Was this class loaded while JvmtiExport::is_early_phase()==true
   bool is_early_klass() {
     return _is_early_klass;
   }
@@ -215,15 +217,26 @@
   bool failed_verification() const                  { return _failed_verification; }
   void set_failed_verification()                    { _failed_verification = true; }
   InstanceKlass* nest_host() const                  { return _nest_host; }
   void set_nest_host(InstanceKlass* nest_host)      { _nest_host = nest_host; }
 
+  bool can_be_preinited() const                     { return _can_be_preinited; }
+  bool has_done_preinit_check() const               { return _has_done_preinit_check; }
   bool is_required_hidden_class() const             { return _is_required_hidden_class; }
   void set_is_required_hidden_class()               { _is_required_hidden_class = true; }
   bool has_scanned_constant_pool() const            { return _has_scanned_constant_pool; }
   void set_has_scanned_constant_pool()              { _has_scanned_constant_pool = true; }
 
+  void set_can_be_preinited(bool v) {
+    _can_be_preinited = v;
+    _has_done_preinit_check = true;
+  }
+  void reset_preinit_check() {
+    _can_be_preinited = false;
+    _has_done_preinit_check = false;
+  }
+
   size_t runtime_info_bytesize() const;
 };
 
 template <typename T>
 inline unsigned DumpTimeSharedClassTable_hash(T* const& k) {
@@ -268,10 +281,11 @@
     }
   }
 
   template<class ITER> void iterate_all_live_classes(ITER* iter) const;
   template<typename Function> void iterate_all_live_classes(Function function) const;
+  template<typename Function> void iterate_all_classes_in_builtin_loaders(Function function) const;
 
 private:
   // It's unsafe to iterate on classes whose loader is dead.
   // Declare these private and don't implement them. This forces users of
   // DumpTimeSharedClassTable to use the iterate_all_live_classes() methods
diff a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp
--- a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp
+++ b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp
@@ -1,8 +1,8 @@
 
 /*
- * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024, 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.
@@ -26,10 +26,11 @@
 #ifndef SHARE_CDS_DUMPTIMECLASSINFO_INLINE_HPP
 #define SHARE_CDS_DUMPTIMECLASSINFO_INLINE_HPP
 
 #include "cds/dumpTimeClassInfo.hpp"
 
+#include "cds/cdsConfig.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "classfile/classLoaderData.inline.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "runtime/safepoint.hpp"
@@ -42,11 +43,14 @@
 template<typename Function>
 void DumpTimeSharedClassTable::iterate_all_live_classes(Function function) const {
   auto wrapper = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
     assert(SafepointSynchronize::is_at_safepoint(), "invariant");
     assert_lock_strong(DumpTimeTable_lock);
-    if (k->is_loader_alive()) {
+    if (CDSConfig::is_dumping_final_static_archive() && !k->is_loaded()) {
+      assert(k->is_shared_unregistered_class(), "must be");
+      function(k, info);
+    } else if (k->is_loader_alive()) {
       function(k, info);
       assert(k->is_loader_alive(), "must not change");
     } else {
       if (!SystemDictionaryShared::is_excluded_class(k)) {
         SystemDictionaryShared::warn_excluded(k, "Class loader not alive");
@@ -55,10 +59,24 @@
     }
   };
   DumpTimeSharedClassTableBaseType::iterate_all(wrapper);
 }
 
+template<typename Function>
+void DumpTimeSharedClassTable::iterate_all_classes_in_builtin_loaders(Function function) const {
+  auto wrapper = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
+    assert_lock_strong(DumpTimeTable_lock);
+    // k->class_loader_data() could be null for unreg classes loaded during CDSConfig::is_dumping_final_static_archive()
+    if (k->class_loader_data() != nullptr && SystemDictionaryShared::is_builtin_loader(k->class_loader_data())) {
+       assert(k->is_loader_alive(), "must be");
+       function(k, info);
+       assert(k->is_loader_alive(), "must be");
+    }
+  };
+  DumpTimeSharedClassTableBaseType::iterate_all(wrapper);
+}
+
 
 template<class ITER>
 void DumpTimeSharedClassTable::iterate_all_live_classes(ITER* iter) const {
   auto function = [&] (InstanceKlass* k, DumpTimeClassInfo& v) {
     iter->do_entry(k, v);
diff a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp
--- a/src/hotspot/share/cds/dynamicArchive.cpp
+++ b/src/hotspot/share/cds/dynamicArchive.cpp
@@ -28,10 +28,11 @@
 #include "cds/archiveHeapWriter.hpp"
 #include "cds/archiveUtils.inline.hpp"
 #include "cds/cds_globals.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/dynamicArchive.hpp"
+#include "cds/metaspaceShared.hpp"
 #include "cds/regeneratedClasses.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderData.inline.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionaryShared.hpp"
@@ -149,12 +150,14 @@
       assert(current_dump_region() == ro_region(), "Must be RO space");
       SymbolTable::write_to_archive(symbols());
 
       ArchiveBuilder::OtherROAllocMark mark;
       SystemDictionaryShared::write_to_archive(false);
+
       DynamicArchive::dump_array_klasses();
       AOTClassLinker::write_to_archive();
+      TrainingData::dump_training_data();
 
       serialized_data = ro_region()->top();
       WriteClosure wc(ro_region());
       ArchiveBuilder::serialize_dynamic_archivable_items(&wc);
     }
@@ -162,10 +165,13 @@
     verify_estimate_size(_estimated_hashtable_bytes, "Hashtables");
 
     log_info(cds)("Adjust lambda proxy class dictionary");
     SystemDictionaryShared::adjust_lambda_proxy_class_dictionary();
 
+    log_info(cds)("Make training data shareable");
+    make_training_data_shareable();
+
     relocate_to_requested();
 
     write_archive(serialized_data);
     release_header();
     DynamicArchive::post_dump();
@@ -177,10 +183,11 @@
   }
 
   virtual void iterate_roots(MetaspaceClosure* it) {
     FileMapInfo::metaspace_pointers_do(it);
     SystemDictionaryShared::dumptime_classes_do(it);
+    TrainingData::iterate_roots(it);
     iterate_primitive_array_klasses(it);
   }
 
   void iterate_primitive_array_klasses(MetaspaceClosure* it) {
     for (int i = T_BOOLEAN; i <= T_LONG; i++) {
@@ -505,13 +512,22 @@
   }
 
   log_info(cds, dynamic)("Preparing for dynamic dump at exit in thread %s", current->name());
 
   JavaThread* THREAD = current; // For TRAPS processing related to link_shared_classes
+
+  {
+    // FIXME-HACK - make sure we have at least one class in the dynamic archive
+    TempNewSymbol class_name = SymbolTable::new_symbol("sun/nio/cs/IBM850"); // unusual class; shouldn't be used by our tests cases.
+    SystemDictionary::resolve_or_null(class_name, Handle(), Handle(), THREAD);
+    guarantee(!HAS_PENDING_EXCEPTION, "must have this class");
+  }
+
   MetaspaceShared::link_shared_classes(false/*not from jcmd*/, THREAD);
   if (!HAS_PENDING_EXCEPTION) {
     // copy shared path table to saved.
+    TrainingData::init_dumptime_table(CHECK); // captures TrainingDataSetLocker
     if (!HAS_PENDING_EXCEPTION) {
       VM_PopulateDynamicDumpSharedSpace op(archive_name);
       VMThread::execute(&op);
       return;
     }
@@ -532,10 +548,11 @@
   assert(CDSConfig::is_using_archive() && RecordDynamicDumpInfo, "already checked in arguments.cpp");
   assert(ArchiveClassesAtExit == nullptr, "already checked in arguments.cpp");
   assert(CDSConfig::is_dumping_dynamic_archive(), "already checked by check_for_dynamic_dump() during VM startup");
   MetaspaceShared::link_shared_classes(true/*from jcmd*/, CHECK);
   // copy shared path table to saved.
+  TrainingData::init_dumptime_table(CHECK); // captures TrainingDataSetLocker
   VM_PopulateDynamicDumpSharedSpace op(archive_name);
   VMThread::execute(&op);
 }
 
 bool DynamicArchive::validate(FileMapInfo* dynamic_info) {
diff a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -224,10 +224,14 @@
   _max_heap_size = MaxHeapSize;
   _use_optimized_module_handling = CDSConfig::is_using_optimized_module_handling();
   _has_aot_linked_classes = CDSConfig::is_dumping_aot_linked_classes();
   _has_full_module_graph = CDSConfig::is_dumping_full_module_graph();
   _has_archived_invokedynamic = CDSConfig::is_dumping_invokedynamic();
+  _has_archived_packages = CDSConfig::is_dumping_packages();
+  _has_archived_protection_domains = CDSConfig::is_dumping_protection_domains();
+  _gc_kind = (int)Universe::heap()->kind();
+  jio_snprintf(_gc_name, sizeof(_gc_name), Universe::heap()->name());
 
   // The following fields are for sanity checks for whether this archive
   // will function correctly with this JVM and the bootclasspath it's
   // invoked with.
 
@@ -315,10 +319,13 @@
   st->print_cr("- allow_archiving_with_java_agent:%d", _allow_archiving_with_java_agent);
   st->print_cr("- use_optimized_module_handling:  %d", _use_optimized_module_handling);
   st->print_cr("- has_full_module_graph           %d", _has_full_module_graph);
   st->print_cr("- has_aot_linked_classes          %d", _has_aot_linked_classes);
   st->print_cr("- has_archived_invokedynamic      %d", _has_archived_invokedynamic);
+  st->print_cr("- has_archived_packages           %d", _has_archived_packages);
+  st->print_cr("- has_archived_protection_domains %d", _has_archived_protection_domains);
+  st->print_cr("- ptrmap_size_in_bits:            " SIZE_FORMAT, _ptrmap_size_in_bits);
 }
 
 void SharedClassPathEntry::init_as_non_existent(const char* path, TRAPS) {
   _type = non_existent_entry;
   set_name(path, CHECK);
@@ -579,10 +586,13 @@
     return 0;
   }
 }
 
 int FileMapInfo::get_module_shared_path_index(Symbol* location) {
+  if (location == nullptr) {
+    return 0; // Used by java/lang/reflect/Proxy$ProxyBuilder
+  }
   if (location->starts_with("jrt:", 4) && get_number_of_shared_paths() > 0) {
     assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image");
     return 0;
   }
 
@@ -1534,11 +1544,11 @@
   return true;
 }
 
 static const char* region_name(int region_index) {
   static const char* names[] = {
-    "rw", "ro", "bm", "hp"
+    "rw", "ro", "bm", "hp", "cc",
   };
   const int num_regions = sizeof(names)/sizeof(names[0]);
   assert(0 <= region_index && region_index < num_regions, "sanity");
 
   return names[region_index];
@@ -1620,10 +1630,13 @@
   if (size > 0) {
     log_info(cds)("Shared file region (%s) %d: " SIZE_FORMAT_W(8)
                    " bytes, addr " INTPTR_FORMAT " file offset 0x%08" PRIxPTR
                    " crc 0x%08x",
                    region_name(region), region, size, p2i(requested_base), _file_offset, crc);
+  } else {
+    log_info(cds)("Shared file region (%s) %d: " SIZE_FORMAT_W(8)
+                  " bytes", region_name(region), region, size);
   }
 
   r->init(region, mapping_offset, size, read_only, allow_exec, crc);
 
   if (base != nullptr) {
@@ -1653,17 +1666,19 @@
   assert(map->size() <= old_size, "sanity");
 
   return first_set;
 }
 
-char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, ArchiveHeapInfo* heap_info,
+char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap,
+                                       CHeapBitMap* cc_ptrmap,
+                                       ArchiveHeapInfo* heap_info,
                                        size_t &size_in_bytes) {
   size_t removed_rw_leading_zeros = remove_bitmap_zeros(rw_ptrmap);
   size_t removed_ro_leading_zeros = remove_bitmap_zeros(ro_ptrmap);
   header()->set_rw_ptrmap_start_pos(removed_rw_leading_zeros);
   header()->set_ro_ptrmap_start_pos(removed_ro_leading_zeros);
-  size_in_bytes = rw_ptrmap->size_in_bytes() + ro_ptrmap->size_in_bytes();
+  size_in_bytes = rw_ptrmap->size_in_bytes() + ro_ptrmap->size_in_bytes() + cc_ptrmap->size_in_bytes();
 
   if (heap_info->is_used()) {
     // Remove leading and trailing zeros
     size_t removed_oop_leading_zeros = remove_bitmap_zeros(heap_info->oopmap());
     size_t removed_ptr_leading_zeros = remove_bitmap_zeros(heap_info->ptrmap());
@@ -1686,10 +1701,13 @@
   written = write_bitmap(rw_ptrmap, buffer, written);
 
   region_at(MetaspaceShared::ro)->init_ptrmap(written, ro_ptrmap->size());
   written = write_bitmap(ro_ptrmap, buffer, written);
 
+  region_at(MetaspaceShared::cc)->init_ptrmap(written, cc_ptrmap->size());
+  written = write_bitmap(cc_ptrmap, buffer, written);
+
   if (heap_info->is_used()) {
     FileMapRegion* r = region_at(MetaspaceShared::hp);
 
     r->init_oopmap(written, heap_info->oopmap()->size());
     written = write_bitmap(heap_info->oopmap(), buffer, written);
@@ -1814,11 +1832,11 @@
   r->set_read_only(false);
   return true;
 }
 
 // Memory map a region in the address space.
-static const char* shared_region_name[] = { "ReadWrite", "ReadOnly", "Bitmap", "Heap" };
+static const char* shared_region_name[] = { "ReadWrite", "ReadOnly", "Bitmap", "Heap", "Code" };
 
 MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char* mapped_base_address, ReservedSpace rs) {
   DEBUG_ONLY(FileMapRegion* last_region = nullptr);
   intx addr_delta = mapped_base_address - header()->requested_base_address();
 
@@ -1917,12 +1935,13 @@
     }
   } else {
     // Note that this may either be a "fresh" mapping into unreserved address
     // space (Windows, first mapping attempt), or a mapping into pre-reserved
     // space (Posix). See also comment in MetaspaceShared::map_archives().
+    bool read_only = r->read_only() && !CDSConfig::is_dumping_final_static_archive();
     char* base = map_memory(_fd, _full_path, r->file_offset(),
-                            requested_addr, size, r->read_only(),
+                            requested_addr, size, read_only,
                             r->allow_exec(), mtClassShared);
     if (base != requested_addr) {
       log_info(cds)("Unable to map %s shared space at " INTPTR_FORMAT,
                     shared_region_name[i], p2i(requested_addr));
       _memory_mapping_failed = true;
@@ -1970,10 +1989,94 @@
                 MetaspaceShared::bm, p2i(r->mapped_base()), p2i(r->mapped_end()),
                 shared_region_name[MetaspaceShared::bm]);
   return bitmap_base;
 }
 
+bool FileMapInfo::map_cached_code_region(ReservedSpace rs) {
+  FileMapRegion* r = region_at(MetaspaceShared::cc);
+  assert(r->used() > 0 && r->used_aligned() == rs.size(), "must be");
+
+  char* requested_base = rs.base();
+  assert(requested_base != nullptr, "should be inside code cache");
+
+  char* mapped_base;
+  if (MetaspaceShared::use_windows_memory_mapping()) {
+    if (!read_region(MetaspaceShared::cc, requested_base, r->used_aligned(), /* do_commit = */ true)) {
+      log_info(cds)("Failed to read cc shared space into reserved space at " INTPTR_FORMAT,
+                    p2i(requested_base));
+      return false;
+    }
+    mapped_base = requested_base;
+  } else {
+    bool read_only = false, allow_exec = false;
+    mapped_base = map_memory(_fd, _full_path, r->file_offset(),
+                             requested_base, r->used_aligned(), read_only, allow_exec, mtClassShared);
+  }
+  if (mapped_base == nullptr) {
+    log_info(cds)("failed to map cached code region");
+    return false;
+  } else {
+    assert(mapped_base == requested_base, "must be");
+    r->set_mapped_from_file(true);
+    r->set_mapped_base(mapped_base);
+    relocate_pointers_in_cached_code_region();
+    log_info(cds)("Mapped static  region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)",
+                  MetaspaceShared::cc, p2i(r->mapped_base()), p2i(r->mapped_end()),
+                  shared_region_name[MetaspaceShared::cc]);
+    return true;
+  }
+}
+
+class CachedCodeRelocator: public BitMapClosure {
+  address _code_requested_base;
+  address* _patch_base;
+  intx _code_delta;
+  intx _metadata_delta;
+
+public:
+  CachedCodeRelocator(address code_requested_base, address code_mapped_base,
+                      intx metadata_delta) {
+    _code_requested_base = code_requested_base;
+    _patch_base = (address*)code_mapped_base;
+    _code_delta = code_mapped_base - code_requested_base;
+    _metadata_delta = metadata_delta;
+  }
+  
+  bool do_bit(size_t offset) {
+    address* p = _patch_base + offset;
+    address requested_ptr = *p;
+    if (requested_ptr < _code_requested_base) {
+      *p = requested_ptr + _metadata_delta;
+    } else {
+      *p = requested_ptr + _code_delta;
+    }
+    return true; // keep iterating
+  }
+};
+
+void FileMapInfo::relocate_pointers_in_cached_code_region() {
+  FileMapRegion* r = region_at(MetaspaceShared::cc);
+  char* bitmap_base = map_bitmap_region();
+
+  BitMapView cc_ptrmap = ptrmap_view(MetaspaceShared::cc);
+  if (cc_ptrmap.size() == 0) {
+    return;
+  }
+
+  address core_regions_requested_base = (address)header()->requested_base_address();
+  address core_regions_mapped_base = (address)header()->mapped_base_address();
+  address cc_region_requested_base = core_regions_requested_base + r->mapping_offset();
+  address cc_region_mapped_base = (address)r->mapped_base();
+
+  size_t max_bits_for_core_regions = pointer_delta(mapped_end(), mapped_base(), // FIXME - renamed to core_regions_mapped_base(), etc
+                                                   sizeof(address));
+
+  CachedCodeRelocator patcher(cc_region_requested_base, cc_region_mapped_base,
+                              core_regions_mapped_base - core_regions_requested_base);
+  cc_ptrmap.iterate(&patcher);
+}
+
 // This is called when we cannot map the archive at the requested[ base address (usually 0x800000000).
 // We relocate all pointers in the 2 core regions (ro, rw).
 bool FileMapInfo::relocate_pointers_in_core_regions(intx addr_delta) {
   log_debug(cds, reloc)("runtime archive relocation start");
   char* bitmap_base = map_bitmap_region();
@@ -2086,11 +2189,11 @@
       }
     }
   }
 
   if (!success) {
-    if (CDSConfig::is_using_aot_linked_classes()) {
+    if (CDSConfig::is_using_aot_linked_classes() && !CDSConfig::is_dumping_final_static_archive()) {
       // It's too late to recover -- we have already committed to use the archived metaspace objects, but
       // the archived heap objects cannot be loaded, so we don't have the archived FMG to guarantee that
       // all AOT-linked classes are visible.
       //
       // We get here because the heap is too small. The app will fail anyway. So let's quit.
@@ -2463,20 +2566,25 @@
     }
     if (JvmtiExport::has_early_vmstart_env()) {
       log_error(cds)("CDS archive has aot-linked classes. It cannot be used when JVMTI early vm start is in use.");
       return false;
     }
-    if (!CDSConfig::is_using_full_module_graph()) {
+    if (!CDSConfig::is_using_full_module_graph() && !CDSConfig::is_dumping_final_static_archive()) {
       log_error(cds)("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used.");
       return false;
     }
 
     const char* prop = Arguments::get_property("java.security.manager");
     if (prop != nullptr && strcmp(prop, "disallow") != 0) {
       log_error(cds)("CDS archive has aot-linked classes. It cannot be used with -Djava.security.manager=%s.", prop);
       return false;
     }
+    if (header()->gc_kind() != (int)Universe::heap()->kind()) {
+      log_error(cds)("CDS archive has aot-linked classes. It cannot be used because GC used during dump time (%s) is not the same as runtime (%s)",
+                     header()->gc_name(), Universe::heap()->name());
+      return false;
+    }
   }
 
   return true;
 }
 
@@ -2591,11 +2699,11 @@
                      _compact_headers          ? "enabled" : "disabled",
                      UseCompactObjectHeaders   ? "enabled" : "disabled");
     return false;
   }
 
-  if (!_use_optimized_module_handling) {
+  if (!_use_optimized_module_handling && !CDSConfig::is_dumping_final_static_archive()) {
     CDSConfig::stop_using_optimized_module_handling();
     log_info(cds)("optimized module handling: disabled because archive was created without optimized module handling");
   }
 
   if (is_static()) {
@@ -2605,10 +2713,16 @@
     }
 
     if (_has_archived_invokedynamic) {
       CDSConfig::set_has_archived_invokedynamic();
     }
+    if (_has_archived_packages) {
+      CDSConfig::set_is_loading_packages();
+    }
+    if (_has_archived_protection_domains) {
+      CDSConfig::set_is_loading_protection_domains();
+    }
   }
 
   return true;
 }
 
diff a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp
--- a/src/hotspot/share/cds/filemap.hpp
+++ b/src/hotspot/share/cds/filemap.hpp
@@ -229,10 +229,15 @@
   bool   _use_optimized_module_handling;// No module-relation VM options were specified, so we can skip
                                         // some expensive operations.
   bool   _has_aot_linked_classes;       // Was the CDS archive created with -XX:+AOTClassLinking
   bool   _has_full_module_graph;        // Does this CDS archive contain the full archived module graph?
   bool   _has_archived_invokedynamic;   // Does the archive have aot-linked invokedynamic CP entries?
+  bool   _has_archived_packages;
+  bool   _has_archived_protection_domains;
+  int    _gc_kind;                      // Universe::heap()->kind();
+  char   _gc_name[32];                  // Universe::heap()->name();
+  size_t _ptrmap_size_in_bits;          // Size of pointer relocation bitmap
   HeapRootSegments _heap_root_segments; // Heap root segments info
   size_t _heap_oopmap_start_pos;        // The first bit in the oopmap corresponds to this position in the heap.
   size_t _heap_ptrmap_start_pos;        // The first bit in the ptrmap corresponds to this position in the heap.
   size_t _rw_ptrmap_start_pos;          // The first bit in the ptrmap corresponds to this position in the rw region
   size_t _ro_ptrmap_start_pos;          // The first bit in the ptrmap corresponds to this position in the ro region
@@ -274,10 +279,13 @@
   char* requested_base_address()           const { return _requested_base_address; }
   char* mapped_base_address()              const { return _mapped_base_address; }
   bool has_platform_or_app_classes()       const { return _has_platform_or_app_classes; }
   bool has_non_jar_in_classpath()          const { return _has_non_jar_in_classpath; }
   bool has_aot_linked_classes()            const { return _has_aot_linked_classes; }
+  int gc_kind()                            const { return _gc_kind; }
+  const char* gc_name()                    const { return _gc_name; }
+  size_t ptrmap_size_in_bits()             const { return _ptrmap_size_in_bits; }
   bool compressed_oops()                   const { return _compressed_oops; }
   bool compressed_class_pointers()         const { return _compressed_class_ptrs; }
   int narrow_klass_pointer_bits()          const { return _narrow_klass_pointer_bits; }
   int narrow_klass_shift()                 const { return _narrow_klass_shift; }
   HeapRootSegments heap_root_segments()    const { return _heap_root_segments; }
@@ -458,11 +466,12 @@
   void  open_for_write();
   void  write_header();
   void  write_region(int region, char* base, size_t size,
                      bool read_only, bool allow_exec);
   size_t remove_bitmap_zeros(CHeapBitMap* map);
-  char* write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, ArchiveHeapInfo* heap_info,
+  char* write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap,
+                            CHeapBitMap* cc_ptrmap, ArchiveHeapInfo* heap_info,
                             size_t &size_in_bytes);
   size_t write_heap_region(ArchiveHeapInfo* heap_info);
   void  write_bytes(const void* buffer, size_t count);
   void  write_bytes_aligned(const void* buffer, size_t count);
   size_t  read_bytes(void* buffer, size_t count);
@@ -474,10 +483,11 @@
   void  patch_heap_embedded_pointers() NOT_CDS_JAVA_HEAP_RETURN;
   bool  has_heap_region()  NOT_CDS_JAVA_HEAP_RETURN_(false);
   MemRegion get_heap_region_requested_range() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion());
   bool  read_region(int i, char* base, size_t size, bool do_commit);
   char* map_bitmap_region();
+  bool map_cached_code_region(ReservedSpace rs);
   void  unmap_region(int i);
   void  close();
   bool  is_open() { return _file_open; }
   ReservedSpace reserve_shared_memory();
 
@@ -578,11 +588,11 @@
   bool  load_heap_region() NOT_CDS_JAVA_HEAP_RETURN_(false);
   bool  map_heap_region() NOT_CDS_JAVA_HEAP_RETURN_(false);
   void  init_heap_region_relocation();
   MapArchiveResult map_region(int i, intx addr_delta, char* mapped_base_address, ReservedSpace rs);
   bool  relocate_pointers_in_core_regions(intx addr_delta);
-
+  void  relocate_pointers_in_cached_code_region();
   static MemRegion _mapped_heap_memregion;
 
 public:
   address heap_region_dumptime_address() NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
   address heap_region_requested_address() NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
diff a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp
--- /dev/null
+++ b/src/hotspot/share/cds/finalImageRecipes.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "cds/aotConstantPoolResolver.hpp"
+#include "cds/archiveBuilder.hpp"
+#include "cds/archiveUtils.inline.hpp"
+#include "cds/cdsConfig.hpp"
+#include "cds/finalImageRecipes.hpp"
+#include "classfile/classLoader.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "classfile/vmClasses.hpp"
+#include "memory/oopFactory.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/constantPool.inline.hpp"
+#include "runtime/handles.inline.hpp"
+
+GrowableArray<InstanceKlass*>* FinalImageRecipes::_tmp_reflect_klasses = nullptr;
+GrowableArray<int>* FinalImageRecipes::_tmp_reflect_flags = nullptr;
+GrowableArray<FinalImageRecipes::TmpDynamicProxyClassInfo>* FinalImageRecipes::_tmp_dynamic_proxy_classes = nullptr;
+static FinalImageRecipes* _final_image_recipes = nullptr;
+
+void* FinalImageRecipes::operator new(size_t size) throw() {
+  return ArchiveBuilder::current()->ro_region_alloc(size);
+}
+
+void FinalImageRecipes::record_recipes_impl() {
+  assert(CDSConfig::is_dumping_preimage_static_archive(), "must be");
+  ResourceMark rm;
+  GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses();
+
+  // The recipes are recorded regardless of CDSConfig::is_dumping_{invokedynamic,dynamic_proxies,reflection_data}().
+  // If some of these options are not enabled, the corresponding recipes will be
+  // ignored during the final image assembly.
+
+  // InvokeDynamic
+  GrowableArray<InstanceKlass*> tmp_indy_klasses;
+  GrowableArray<Array<int>*> tmp_indy_cp_indices;
+  int total_indys_to_resolve = 0;
+  for (int i = 0; i < klasses->length(); i++) {
+    Klass* k = klasses->at(i);
+    if (k->is_instance_klass()) {
+      InstanceKlass* ik = InstanceKlass::cast(k);
+      GrowableArray<int> indices;
+
+      if (ik->constants()->cache() != nullptr) {
+        Array<ResolvedIndyEntry>* tmp_indy_entries = ik->constants()->cache()->resolved_indy_entries();
+        if (tmp_indy_entries != nullptr) {
+          for (int i = 0; i < tmp_indy_entries->length(); i++) {
+            ResolvedIndyEntry* rie = tmp_indy_entries->adr_at(i);
+            int cp_index = rie->constant_pool_index();
+            if (rie->is_resolved()) {
+              indices.append(cp_index);
+            }
+          }
+        }
+      }
+
+      if (indices.length() > 0) {
+        tmp_indy_klasses.append(ArchiveBuilder::current()->get_buffered_addr(ik));
+        tmp_indy_cp_indices.append(ArchiveUtils::archive_array(&indices));
+        total_indys_to_resolve += indices.length();
+      }
+    }
+  }
+
+  assert(tmp_indy_klasses.length() == tmp_indy_cp_indices.length(), "must be");
+  if (tmp_indy_klasses.length() > 0) {
+    _indy_klasses = ArchiveUtils::archive_array(&tmp_indy_klasses);
+    _indy_cp_indices = ArchiveUtils::archive_array(&tmp_indy_cp_indices);
+
+    ArchivePtrMarker::mark_pointer(&_indy_klasses);
+    ArchivePtrMarker::mark_pointer(&_indy_cp_indices);
+  }
+  log_info(cds)("%d indies in %d classes will be resolved in final CDS image", total_indys_to_resolve, tmp_indy_klasses.length());
+
+  // Reflection Data
+  int reflect_count = 0;
+  if (_tmp_reflect_klasses != nullptr) {
+    for (int i = _tmp_reflect_klasses->length() - 1; i >= 0; i--) {
+      InstanceKlass* ik = _tmp_reflect_klasses->at(i);
+      if (SystemDictionaryShared::is_excluded_class(ik)) {
+        _tmp_reflect_klasses->remove_at(i);
+        _tmp_reflect_flags->remove_at(i);
+      } else {
+        _tmp_reflect_klasses->at_put(i, ArchiveBuilder::current()->get_buffered_addr(ik));
+      }
+    }
+    if (_tmp_reflect_klasses->length() > 0) {
+      _reflect_klasses = ArchiveUtils::archive_array(_tmp_reflect_klasses);
+      _reflect_flags = ArchiveUtils::archive_array(_tmp_reflect_flags);
+
+      ArchivePtrMarker::mark_pointer(&_reflect_klasses);
+      ArchivePtrMarker::mark_pointer(&_reflect_flags);
+      reflect_count = _tmp_reflect_klasses->length();
+    }
+  }
+  log_info(cds)("ReflectionData of %d classes will be archived in final CDS image", reflect_count);
+
+  // Dynamic Proxies
+  if (_tmp_dynamic_proxy_classes != nullptr) {
+    int len = _tmp_dynamic_proxy_classes->length();
+    _dynamic_proxy_classes = ArchiveBuilder::new_ro_array<DynamicProxyClassInfo>(len);
+    ArchivePtrMarker::mark_pointer(&_dynamic_proxy_classes);
+    for (int i = 0; i < len; i++) {
+      TmpDynamicProxyClassInfo* tmp_info = _tmp_dynamic_proxy_classes->adr_at(i);
+      DynamicProxyClassInfo* info = _dynamic_proxy_classes->adr_at(i);
+      info->_loader_type = tmp_info->_loader_type;
+      info->_access_flags = tmp_info->_access_flags;
+      info->_proxy_name = ArchiveBuilder::current()->ro_strdup(tmp_info->_proxy_name);
+
+      ResourceMark rm;
+      GrowableArray<Klass*> buffered_interfaces;
+      for (int j = 0; j < tmp_info->_interfaces->length(); j++) {
+        // FIXME: tmp_info->_interfaces->at(j) could be excluded from archive!
+        buffered_interfaces.append(ArchiveBuilder::current()->get_buffered_addr(tmp_info->_interfaces->at(j)));
+      }
+      info->_interfaces = ArchiveUtils::archive_array(&buffered_interfaces);
+
+      ArchivePtrMarker::mark_pointer(&info->_proxy_name);
+      ArchivePtrMarker::mark_pointer(&info->_interfaces);
+      ArchiveBuilder::alloc_stats()->record_dynamic_proxy_class();
+    }
+  }
+}
+
+void FinalImageRecipes::apply_recipes_for_invokedynamic(TRAPS) {
+  assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+
+  if (CDSConfig::is_dumping_invokedynamic() && _indy_klasses != nullptr) {
+    assert(_indy_cp_indices != nullptr, "must be");
+    for (int i = 0; i < _indy_klasses->length(); i++) {
+      InstanceKlass* ik = _indy_klasses->at(i);
+      ConstantPool* cp = ik->constants();
+      Array<int>* cp_indices = _indy_cp_indices->at(i);
+      GrowableArray<bool> preresolve_list(cp->length(), cp->length(), false);
+      for (int j = 0; j < cp_indices->length(); j++) {
+        preresolve_list.at_put(cp_indices->at(j), true);
+      }
+      AOTConstantPoolResolver::preresolve_indy_cp_entries(THREAD, ik, &preresolve_list);
+    }
+  }
+}
+
+void FinalImageRecipes::apply_recipes_for_reflection_data(JavaThread* current) {
+  assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+
+  if (CDSConfig::is_dumping_reflection_data() && _reflect_klasses != nullptr) {
+    assert(_reflect_flags != nullptr, "must be");
+    for (int i = 0; i < _reflect_klasses->length(); i++) {
+      InstanceKlass* ik = _reflect_klasses->at(i);
+      int rd_flags = _reflect_flags->at(i);
+      AOTConstantPoolResolver::generate_reflection_data(current, ik, rd_flags);
+    }
+  }
+}
+
+void FinalImageRecipes::add_reflection_data_flags(InstanceKlass* ik, TRAPS) {
+  assert(CDSConfig::is_dumping_preimage_static_archive(), "must be");
+  if (SystemDictionaryShared::is_builtin_loader(ik->class_loader_data()) && !ik->is_hidden() &&
+      java_lang_Class::has_reflection_data(ik->java_mirror())) {
+    int rd_flags = AOTConstantPoolResolver::class_reflection_data_flags(ik, CHECK);
+    if (_tmp_reflect_klasses == nullptr) {
+      _tmp_reflect_klasses = new (mtClassShared) GrowableArray<InstanceKlass*>(100, mtClassShared);
+      _tmp_reflect_flags = new (mtClassShared) GrowableArray<int>(100, mtClassShared);
+    }
+    _tmp_reflect_klasses->append(ik);
+    _tmp_reflect_flags->append(rd_flags);
+  }
+}
+
+void FinalImageRecipes::add_dynamic_proxy_class(oop loader, const char* proxy_name, objArrayOop interfaces, int access_flags) {
+  int loader_type;
+  if (loader == nullptr) {
+    loader_type = ClassLoader::BOOT_LOADER;
+  } else if (loader == SystemDictionary::java_platform_loader()) {
+    loader_type = ClassLoader::PLATFORM_LOADER;
+  } else if (loader == SystemDictionary::java_system_loader()) {
+    loader_type = ClassLoader::APP_LOADER;
+  } else {
+    return;
+  }
+
+  if (_tmp_dynamic_proxy_classes == nullptr) {
+    _tmp_dynamic_proxy_classes = new (mtClassShared) GrowableArray<TmpDynamicProxyClassInfo>(32, mtClassShared);
+  }
+
+  log_info(cds, dynamic, proxy)("Adding proxy name %s", proxy_name);
+
+  TmpDynamicProxyClassInfo info;
+  info._loader_type = loader_type;
+  info._access_flags = access_flags;
+  info._proxy_name = os::strdup(proxy_name);
+  info._interfaces = new (mtClassShared) GrowableArray<Klass*>(interfaces->length(), mtClassShared);
+  for (int i = 0; i < interfaces->length(); i++) {
+    Klass* intf = java_lang_Class::as_Klass(interfaces->obj_at(i));
+    info._interfaces->append(intf);
+
+    if (log_is_enabled(Info, cds, dynamic, proxy)) {
+      ResourceMark rm;
+      log_info(cds, dynamic, proxy)("interface[%d] = %s", i, intf->external_name());
+    }
+  }
+  _tmp_dynamic_proxy_classes->append(info);
+}
+
+void FinalImageRecipes::apply_recipes_for_dynamic_proxies(TRAPS) {
+  if (CDSConfig::is_dumping_dynamic_proxies() && _dynamic_proxy_classes != nullptr) {
+    for (int proxy_index = 0; proxy_index < _dynamic_proxy_classes->length(); proxy_index++) {
+      DynamicProxyClassInfo* info = _dynamic_proxy_classes->adr_at(proxy_index);
+
+      Handle loader(THREAD, ArchiveUtils::builtin_loader_from_type(info->_loader_type));
+
+      oop proxy_name_oop = java_lang_String::create_oop_from_str(info->_proxy_name, CHECK);
+      Handle proxy_name(THREAD, proxy_name_oop);
+
+      int num_intfs = info->_interfaces->length();
+      objArrayOop interfaces_oop = oopFactory::new_objArray(vmClasses::Class_klass(), num_intfs, CHECK);
+      objArrayHandle interfaces(THREAD, interfaces_oop);
+      for (int intf_index = 0; intf_index < num_intfs; intf_index++) {
+        Klass* k = info->_interfaces->at(intf_index);
+        assert(k->java_mirror() != nullptr, "must be loaded");
+        interfaces()->obj_at_put(intf_index, k->java_mirror());
+      }
+
+      AOTConstantPoolResolver::define_dynamic_proxy_class(loader, proxy_name, interfaces, info->_access_flags, CHECK);
+    }
+  }
+}
+
+void FinalImageRecipes::record_recipes() {
+  _final_image_recipes = new FinalImageRecipes();
+  _final_image_recipes->record_recipes_impl();
+}
+
+void FinalImageRecipes::apply_recipes(TRAPS) {
+  assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+
+  if (_final_image_recipes != nullptr) {
+    _final_image_recipes->apply_recipes_for_invokedynamic(CHECK);
+    _final_image_recipes->apply_recipes_for_reflection_data(THREAD);
+    _final_image_recipes->apply_recipes_for_dynamic_proxies(CHECK);
+  }
+
+  // Set it to null as we don't need to write this table into the final image.
+  _final_image_recipes = nullptr;
+}
+
+void FinalImageRecipes::serialize(SerializeClosure* soc, bool is_static_archive) {
+  if (is_static_archive) {
+    soc->do_ptr((void**)&_final_image_recipes);
+  }
+}
diff a/src/hotspot/share/cds/finalImageRecipes.hpp b/src/hotspot/share/cds/finalImageRecipes.hpp
--- /dev/null
+++ b/src/hotspot/share/cds/finalImageRecipes.hpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+#ifndef SHARE_CDS_FINALIMAGERECIPES_HPP
+#define SHARE_CDS_FINALIMAGERECIPES_HPP
+
+#include "oops/oopsHierarchy.hpp"
+#include "utilities/exceptions.hpp"
+
+class InstanceKlass;
+class Klass;
+
+template <typename T> class GrowableArray;
+template <typename T> class Array;
+
+// This class is used only by the "one step training workflow". It records the
+// "recipes" for creating the final CDS image.
+// - The recipes are recorded when CDSConfig::is_dumping_preimage_static_archive() is true;
+// - The recipes are applied when CDSConfig::is_dumping_final_static_archive() is true;
+class FinalImageRecipes {
+  // The classes who have resolved at least one indy CP entry during the training run.
+  // _indy_cp_indices[i] is a list of all resolved CP entries for _indy_klasses[i].
+  Array<InstanceKlass*>* _indy_klasses;
+  Array<Array<int>*>*    _indy_cp_indices;
+
+  // The RefectionData for  _reflect_klasses[i] should be initialized with _reflect_flags[i]
+  Array<InstanceKlass*>* _reflect_klasses;
+  Array<int>*            _reflect_flags;
+
+  static GrowableArray<InstanceKlass*>* _tmp_reflect_klasses;
+  static GrowableArray<int>* _tmp_reflect_flags;
+
+  struct TmpDynamicProxyClassInfo {
+    int _loader_type;
+    int _access_flags;
+    const char* _proxy_name;
+    GrowableArray<Klass*>* _interfaces;
+  };
+
+  struct DynamicProxyClassInfo {
+    int _loader_type;
+    int _access_flags;
+    const char* _proxy_name;
+    Array<Klass*>* _interfaces;
+  };
+
+  Array<DynamicProxyClassInfo>* _dynamic_proxy_classes;
+
+  static GrowableArray<TmpDynamicProxyClassInfo>* _tmp_dynamic_proxy_classes;
+
+  FinalImageRecipes() : _indy_klasses(nullptr), _indy_cp_indices(nullptr),
+                        _reflect_klasses(nullptr), _reflect_flags(nullptr),
+                        _dynamic_proxy_classes(nullptr) {}
+
+  void* operator new(size_t size) throw();
+
+  // Called when dumping preimage
+  void record_recipes_impl();
+
+  // Called when dumping final image
+  void apply_recipes_for_dynamic_proxies(TRAPS);
+  void apply_recipes_for_invokedynamic(TRAPS);
+  void apply_recipes_for_reflection_data(JavaThread* current);
+
+public:
+  static void serialize(SerializeClosure* soc, bool is_static_archive);
+
+  // Called when dumping preimage
+  static void add_dynamic_proxy_class(oop loader, const char* proxy_name, objArrayOop interfaces, int access_flags);
+  static void add_reflection_data_flags(InstanceKlass* ik, TRAPS);
+  static void record_recipes();
+
+  // Called when dumping final image
+  static void apply_recipes(TRAPS);
+};
+
+#endif // SHARE_CDS_FINALIMAGERECIPES_HPP
diff a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp
--- a/src/hotspot/share/cds/heapShared.cpp
+++ b/src/hotspot/share/cds/heapShared.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, 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.
@@ -26,10 +26,11 @@
 #include "cds/aotClassInitializer.hpp"
 #include "cds/archiveBuilder.hpp"
 #include "cds/archiveHeapLoader.hpp"
 #include "cds/archiveHeapWriter.hpp"
 #include "cds/archiveUtils.hpp"
+#include "cds/cdsAccess.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/cdsEnumKlass.hpp"
 #include "cds/cdsHeapVerifier.hpp"
 #include "cds/heapShared.hpp"
 #include "cds/metaspaceShared.hpp"
@@ -84,10 +85,31 @@
   bool valid() {
     return klass_name != nullptr;
   }
 };
 
+class HeapShared::ArchivingObjectMark : public StackObj {
+public:
+  ArchivingObjectMark(oop obj) {
+    _trace->push(obj);
+  }
+  ~ArchivingObjectMark() {
+    _trace->pop();
+  }
+};
+
+class HeapShared::ContextMark : public StackObj {
+  ResourceMark rm;
+public:
+  ContextMark(const char* c) : rm{} {
+    _context->push(c);
+  }
+  ~ContextMark() {
+    _context->pop();
+  }
+};
+
 bool HeapShared::_disable_writing = false;
 DumpedInternedStrings *HeapShared::_dumped_interned_strings = nullptr;
 
 size_t HeapShared::_alloc_count[HeapShared::ALLOC_STAT_SLOTS];
 size_t HeapShared::_alloc_size[HeapShared::ALLOC_STAT_SLOTS];
@@ -118,10 +140,11 @@
   {"jdk/internal/module/ArchivedModuleGraph",     "archivedModuleGraph"},
   {"java/util/ImmutableCollections",              "archivedObjects"},
   {"java/lang/ModuleLayer",                       "EMPTY_LAYER"},
   {"java/lang/module/Configuration",              "EMPTY_CONFIGURATION"},
   {"jdk/internal/math/FDBigInteger",              "archivedCaches"},
+  {"java/lang/reflect/Proxy$ProxyBuilder",        "archivedData"},    // FIXME -- requires AOTClassLinking
 
 #ifndef PRODUCT
   {nullptr, nullptr}, // Extra slot for -XX:ArchiveHeapTestClass
 #endif
   {nullptr, nullptr},
@@ -135,11 +158,13 @@
   {nullptr, nullptr},
 };
 
 KlassSubGraphInfo* HeapShared::_dump_time_special_subgraph;
 ArchivedKlassSubGraphInfoRecord* HeapShared::_run_time_special_subgraph;
-GrowableArrayCHeap<oop, mtClassShared>* HeapShared::_pending_roots = nullptr;
+GrowableArrayCHeap<OopHandle, mtClassShared>* HeapShared::_pending_roots = nullptr;
+GrowableArrayCHeap<oop, mtClassShared>* HeapShared::_trace = nullptr;
+GrowableArrayCHeap<const char*, mtClassShared>* HeapShared::_context = nullptr;
 GrowableArrayCHeap<OopHandle, mtClassShared>* HeapShared::_root_segments;
 int HeapShared::_root_segment_max_size_elems;
 OopHandle HeapShared::_scratch_basic_type_mirrors[T_VOID+1];
 MetaspaceObjToOopHandleTable* HeapShared::_scratch_java_mirror_table = nullptr;
 MetaspaceObjToOopHandleTable* HeapShared::_scratch_references_table = nullptr;
@@ -223,18 +248,19 @@
 
   // No GC should happen since we aren't scanning _pending_roots.
   assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
 
   if (_pending_roots == nullptr) {
-    _pending_roots = new GrowableArrayCHeap<oop, mtClassShared>(500);
+    _pending_roots = new GrowableArrayCHeap<OopHandle, mtClassShared>(500);
   }
 
-  return _pending_roots->append(obj);
+  OopHandle oh(Universe::vm_global(), obj);
+  return _pending_roots->append(oh);
 }
 
 objArrayOop HeapShared::root_segment(int segment_idx) {
-  if (CDSConfig::is_dumping_heap()) {
+  if (CDSConfig::is_dumping_heap() && !CDSConfig::is_dumping_final_static_archive()) {
     assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
     if (!HeapShared::can_write()) {
       return nullptr;
     }
   } else {
@@ -244,10 +270,170 @@
   objArrayOop segment = (objArrayOop)_root_segments->at(segment_idx).resolve();
   assert(segment != nullptr, "should have been initialized");
   return segment;
 }
 
+inline unsigned int oop_handle_hash(const OopHandle& oh) {
+  oop o = oh.resolve();
+  if (o == nullptr) {
+    return 0;
+  } else {
+    return o->identity_hash();
+  }
+}
+
+inline bool oop_handle_equals(const OopHandle& a, const OopHandle& b) {
+  return a.resolve() == b.resolve();
+}
+
+class OrigToScratchObjectTable: public ResourceHashtable<OopHandle, OopHandle,
+    36137, // prime number
+    AnyObj::C_HEAP,
+    mtClassShared,
+    oop_handle_hash,
+    oop_handle_equals> {};
+
+static OrigToScratchObjectTable* _orig_to_scratch_object_table = nullptr;
+
+void HeapShared::track_scratch_object(oop orig_obj, oop scratch_obj) {
+  MutexLocker ml(ArchivedObjectTables_lock, Mutex::_no_safepoint_check_flag);
+  if (_orig_to_scratch_object_table == nullptr) {
+    _orig_to_scratch_object_table = new (mtClass)OrigToScratchObjectTable();
+  }
+
+  OopHandle orig_h(Universe::vm_global(), orig_obj);
+  OopHandle scratch_h(Universe::vm_global(), scratch_obj);
+  _orig_to_scratch_object_table->put_when_absent(orig_h, scratch_h);
+}
+
+oop HeapShared::orig_to_scratch_object(oop orig_obj) {
+  MutexLocker ml(ArchivedObjectTables_lock, Mutex::_no_safepoint_check_flag);
+  if (_orig_to_scratch_object_table != nullptr) {
+    OopHandle orig(&orig_obj);
+    OopHandle* v = _orig_to_scratch_object_table->get(orig);
+    if (v != nullptr) {
+      return v->resolve();
+    }
+  }
+  return nullptr;
+}
+
+// Permanent oops are used to support AOT-compiled methods, which may have in-line references
+// to Strings and MH oops.
+//
+// At runtime, these oops are stored in _runtime_permanent_oops (which keeps them alive forever)
+// and are accssed vis CDSAccess::get_archived_object(int).
+struct PermanentOopInfo {
+  int _index;       // Gets assigned only if HeapShared::get_archived_object_permanent_index() has been called on the object
+  int _heap_offset; // Offset of the object from the bottom of the archived heap.
+  PermanentOopInfo(int index, int heap_offset) : _index(index), _heap_offset(heap_offset) {}
+};
+
+class PermanentOopTable: public ResourceHashtable<OopHandle, PermanentOopInfo,
+    36137, // prime number
+    AnyObj::C_HEAP,
+    mtClassShared,
+    oop_handle_hash,
+    oop_handle_equals> {};
+
+static int _dumptime_permanent_oop_count = 0;
+static PermanentOopTable* _dumptime_permanent_oop_table = nullptr;
+static GrowableArrayCHeap<OopHandle, mtClassShared>* _runtime_permanent_oops = nullptr;
+
+// ArchiveHeapWriter adds each archived heap object to _dumptime_permanent_oop_table,
+// so we can remember their offset (from the bottom of the archived heap).
+void HeapShared::add_to_permanent_oop_table(oop obj, int offset) {
+  assert_at_safepoint();
+  if (_dumptime_permanent_oop_table == nullptr) {
+    _dumptime_permanent_oop_table = new (mtClass)PermanentOopTable();
+  }
+
+  PermanentOopInfo info(-1, offset);
+  OopHandle oh(Universe::vm_global(), obj);
+  _dumptime_permanent_oop_table->put_when_absent(oh, info);
+}
+
+// A permanent index is assigned to an archived object ONLY when
+// the AOT compiler calls this function.
+int HeapShared::get_archived_object_permanent_index(oop obj) {
+  MutexLocker ml(ArchivedObjectTables_lock, Mutex::_no_safepoint_check_flag);
+
+  if (!CDSConfig::is_dumping_heap()) {
+    return -1; // Called by the Leyden old workflow
+  }
+  if (_dumptime_permanent_oop_table == nullptr) {
+    return -1;
+  }
+
+  if (_orig_to_scratch_object_table != nullptr) {
+    OopHandle orig(&obj);
+    OopHandle* v = _orig_to_scratch_object_table->get(orig);
+    if (v != nullptr) {
+      obj = v->resolve();
+    }
+  }
+
+  OopHandle tmp(&obj);
+  PermanentOopInfo* info = _dumptime_permanent_oop_table->get(tmp);
+  if (info == nullptr) {
+    return -1;
+  } else {
+    if (info->_index < 0) {
+      info->_index = _dumptime_permanent_oop_count++;
+    }
+    return info->_index;
+  }
+}
+
+oop HeapShared::get_archived_object(int permanent_index) {
+  assert(permanent_index >= 0, "sanity");
+  assert(ArchiveHeapLoader::is_in_use(), "sanity");
+  assert(_runtime_permanent_oops != nullptr, "sanity");
+
+  return _runtime_permanent_oops->at(permanent_index).resolve();
+}
+
+// Remember all archived heap objects that have a permanent index.
+//   table[i] = offset of oop whose permanent index is i.
+void CachedCodeDirectoryInternal::dumptime_init_internal() {
+  const int count = _dumptime_permanent_oop_count;
+  int* table = (int*)CDSAccess::allocate_from_code_cache(count * sizeof(int));
+  for (int i = 0; i < count; i++) {
+    table[count] = -1;
+  }
+  _dumptime_permanent_oop_table->iterate([&](OopHandle o, PermanentOopInfo& info) {
+    int index = info._index;
+    if (index >= 0) {
+      assert(index < count, "sanity");
+      table[index] = info._heap_offset;
+    }
+    return true; // continue
+  });
+
+  for (int i = 0; i < count; i++) {
+    assert(table[i] >= 0, "must be");
+  }
+
+  log_info(cds)("Dumped %d permanent oops", count);
+
+  _permanent_oop_count = count;
+  CDSAccess::set_pointer(&_permanent_oop_offsets, table);
+}
+
+// This is called during the bootstrap of the production run, before any GC can happen.
+// Record each permanent oop in a OopHandle for GC safety.
+void CachedCodeDirectoryInternal::runtime_init_internal() {
+  int count = _permanent_oop_count;
+  int* table = _permanent_oop_offsets;
+  _runtime_permanent_oops = new GrowableArrayCHeap<OopHandle, mtClassShared>();
+  for (int i = 0; i < count; i++) {
+    oop obj = ArchiveHeapLoader::oop_from_offset(table[i]);
+    OopHandle oh(Universe::vm_global(), obj);
+    _runtime_permanent_oops->append(oh);
+  }
+};
+
 void HeapShared::get_segment_indexes(int idx, int& seg_idx, int& int_idx) {
   assert(_root_segment_max_size_elems > 0, "sanity");
 
   // Try to avoid divisions for the common case.
   if (idx < _root_segment_max_size_elems) {
@@ -365,10 +551,13 @@
     }
   }
 };
 
 void HeapShared::add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) {
+  if (_scratch_references_table == nullptr) {
+    _scratch_references_table = new (mtClass)MetaspaceObjToOopHandleTable();
+  }
   _scratch_references_table->set_oop(src, dest);
 }
 
 objArrayOop HeapShared::scratch_resolved_references(ConstantPool* src) {
   return (objArrayOop)_scratch_references_table->get_oop(src);
@@ -378,14 +567,17 @@
   for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
     BasicType bt = (BasicType)i;
     if (!is_reference_type(bt)) {
       oop m = java_lang_Class::create_basic_type_mirror(type2name(bt), bt, CHECK);
       _scratch_basic_type_mirrors[i] = OopHandle(Universe::vm_global(), m);
+      track_scratch_object(Universe::java_mirror(bt), m);
     }
   }
   _scratch_java_mirror_table = new (mtClass)MetaspaceObjToOopHandleTable();
-  _scratch_references_table = new (mtClass)MetaspaceObjToOopHandleTable();
+  if (_scratch_references_table == nullptr) {
+    _scratch_references_table = new (mtClass)MetaspaceObjToOopHandleTable();
+  }
 }
 
 // Given java_mirror that represents a (primitive or reference) type T,
 // return the "scratch" version that represents the same type T.
 // Note that if java_mirror will be returned if it's already a
@@ -420,10 +612,11 @@
 oop HeapShared::scratch_java_mirror(Klass* k) {
   return _scratch_java_mirror_table->get_oop(k);
 }
 
 void HeapShared::set_scratch_java_mirror(Klass* k, oop mirror) {
+  track_scratch_object(k->java_mirror(), mirror);
   _scratch_java_mirror_table->set_oop(k, mirror);
 }
 
 void HeapShared::remove_scratch_objects(Klass* k) {
   // Klass is being deallocated. Java mirror can still be alive, and it should not
@@ -435,10 +628,19 @@
   }
   _scratch_java_mirror_table->remove_oop(k);
   if (k->is_instance_klass()) {
     _scratch_references_table->remove(InstanceKlass::cast(k)->constants());
   }
+  if (mirror != nullptr) {
+    OopHandle tmp(&mirror);
+    OopHandle* v = _orig_to_scratch_object_table->get(tmp);
+    if (v != nullptr) {
+      oop scratch_mirror = v->resolve();
+      java_lang_Class::set_klass(scratch_mirror, nullptr);
+      _orig_to_scratch_object_table->remove(tmp);
+    }
+  }
 }
 
 //TODO: we eventually want a more direct test for these kinds of things.
 //For example the JVM could record some bit of context from the creation
 //of the klass, such as who called the hidden class factory.  Using
@@ -769,10 +971,12 @@
     NoSafepointVerifier nsv;
 
     // The special subgraph doesn't belong to any class. We use Object_klass() here just
     // for convenience.
     _dump_time_special_subgraph = init_subgraph_info(vmClasses::Object_klass(), false);
+    _trace = new GrowableArrayCHeap<oop, mtClassShared>(250);
+    _context = new GrowableArrayCHeap<const char*, mtClassShared>(250);
 
     // Cache for recording where the archived objects are copied to
     create_archived_object_cache();
 
     if (UseCompressedOops || UseG1GC) {
@@ -782,15 +986,21 @@
                     UseCompressedOops ? p2i(CompressedOops::end()) :
                                         p2i((address)G1CollectedHeap::heap()->reserved().end()));
     }
     copy_objects();
 
-    CDSHeapVerifier::verify();
+    if (!SkipArchiveHeapVerification) {
+      CDSHeapVerifier::verify();
+    }
     check_special_subgraph_classes();
   }
 
-  ArchiveHeapWriter::write(_pending_roots, heap_info);
+  GrowableArrayCHeap<oop, mtClassShared>* roots = new GrowableArrayCHeap<oop, mtClassShared>(_pending_roots->length());
+  for (int i = 0; i < _pending_roots->length(); i++) {
+    roots->append(_pending_roots->at(i).resolve());
+  }
+  ArchiveHeapWriter::write(roots, heap_info);
 }
 
 void HeapShared::copy_interned_strings() {
   init_seen_objects_table();
 
@@ -1147,10 +1357,14 @@
   _subgraph_object_klasses->append_if_missing(buffered_k);
   _has_non_early_klasses |= is_non_early_klass(orig_k);
 }
 
 void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) {
+  if (CDSConfig::is_dumping_invokedynamic()) {
+    // FIXME -- this allows LambdaProxy classes
+    return;
+  }
   if (ik->module()->name() == vmSymbols::java_base()) {
     assert(ik->package() != nullptr, "classes in java.base cannot be in unnamed package");
     return;
   }
 
@@ -1384,10 +1598,23 @@
 void HeapShared::resolve_classes(JavaThread* current) {
   assert(CDSConfig::is_using_archive(), "runtime only!");
   if (!ArchiveHeapLoader::is_in_use()) {
     return; // nothing to do
   }
+
+  if (!CDSConfig::is_using_aot_linked_classes()) {
+    assert( _run_time_special_subgraph != nullptr, "must be");
+    Array<Klass*>* klasses = _run_time_special_subgraph->subgraph_object_klasses();
+    if (klasses != nullptr) {
+      for (int i = 0; i < klasses->length(); i++) {
+        Klass* k = klasses->at(i);
+        ExceptionMark em(current); // no exception can happen here
+        resolve_or_init(k, /*do_init*/false, current);
+      }
+    }
+  }
+
   resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields);
   resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields);
 }
 
 void HeapShared::resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]) {
@@ -1752,27 +1979,52 @@
     vmClasses::Long_klass()->initialize(CHECK);
     vmClasses::Void_klass()->initialize(CHECK);
   }
 }
 
+void HeapShared::exit_on_error() {
+  if (_context != nullptr) {
+    ResourceMark rm;
+    LogStream ls(Log(cds, heap)::error());
+    ls.print_cr("Context");
+    for (int i = 0; i < _context->length(); i++) {
+      const char* s = _context->at(i);
+      ls.print_cr("- %s", s);
+    }
+  }
+  if (_trace != nullptr) {
+    ResourceMark rm;
+    LogStream ls(Log(cds, heap)::error());
+    ls.print_cr("Reference trace");
+    for (int i = 0; i < _trace->length(); i++) {
+      oop orig_obj = _trace->at(i);
+      ls.print_cr("[%d] ========================================", i);
+      orig_obj->print_on(&ls);
+      ls.cr();
+    }
+  }
+  MetaspaceShared::unrecoverable_writing_error();
+}
+
 // (1) If orig_obj has not been archived yet, archive it.
 // (2) If orig_obj has not been seen yet (since start_recording_subgraph() was called),
 //     trace all  objects that are reachable from it, and make sure these objects are archived.
 // (3) Record the klasses of all orig_obj and all reachable objects.
 bool HeapShared::archive_reachable_objects_from(int level,
                                                 KlassSubGraphInfo* subgraph_info,
                                                 oop orig_obj) {
+  ArchivingObjectMark mark(orig_obj);
   assert(orig_obj != nullptr, "must be");
 
   if (!JavaClasses::is_supported_for_archiving(orig_obj)) {
     // This object has injected fields that cannot be supported easily, so we disallow them for now.
     // If you get an error here, you probably made a change in the JDK library that has added
     // these objects that are referenced (directly or indirectly) by static fields.
     ResourceMark rm;
     log_error(cds, heap)("Cannot archive object " PTR_FORMAT " of class %s", p2i(orig_obj), orig_obj->klass()->external_name());
     debug_trace();
-    MetaspaceShared::unrecoverable_writing_error();
+    exit_on_error();
   }
 
   if (log_is_enabled(Debug, cds, heap) && java_lang_Class::is_instance(orig_obj)) {
     ResourceMark rm;
     LogTarget(Debug, cds, heap) log;
@@ -1844,11 +2096,11 @@
         return false;
       } else {
         // We don't know how to handle an object that has been archived, but some of its reachable
         // objects cannot be archived. Bail out for now. We might need to fix this in the future if
         // we have a real use case.
-        MetaspaceShared::unrecoverable_writing_error();
+        exit_on_error();
       }
     }
   }
 
   Klass *orig_k = orig_obj->klass();
@@ -2293,20 +2545,22 @@
   for (int i = 0; fields[i].valid(); ) {
     ArchivableStaticFieldInfo* info = &fields[i];
     const char* klass_name = info->klass_name;
     start_recording_subgraph(info->klass, klass_name, is_full_module_graph);
 
+    ContextMark cm(klass_name);
     // If you have specified consecutive fields of the same klass in
     // fields[], these will be archived in the same
     // {start_recording_subgraph ... done_recording_subgraph} pass to
     // save time.
     for (; fields[i].valid(); i++) {
       ArchivableStaticFieldInfo* f = &fields[i];
       if (f->klass_name != klass_name) {
         break;
       }
 
+      ContextMark cm(f->field_name);
       archive_reachable_objects_from_static_field(f->klass, f->klass_name,
                                                   f->offset, f->field_name);
     }
     done_recording_subgraph(info->klass, klass_name);
   }
diff a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp
--- a/src/hotspot/share/cds/heapShared.hpp
+++ b/src/hotspot/share/cds/heapShared.hpp
@@ -23,10 +23,11 @@
  */
 
 #ifndef SHARE_CDS_HEAPSHARED_HPP
 #define SHARE_CDS_HEAPSHARED_HPP
 
+#include "cds/cds_globals.hpp"
 #include "cds/dumpTimeClassInfo.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "classfile/compactHashtable.hpp"
 #include "classfile/javaClasses.hpp"
 #include "gc/shared/gc_globals.hpp"
@@ -307,12 +308,14 @@
   //    - ConstantPool::resolved_references()
   //    - Universe::<xxx>_exception_instance()
   static KlassSubGraphInfo* _dump_time_special_subgraph;              // for collecting info during dump time
   static ArchivedKlassSubGraphInfoRecord* _run_time_special_subgraph; // for initializing classes during run time.
 
-  static GrowableArrayCHeap<oop, mtClassShared>* _pending_roots;
+  static GrowableArrayCHeap<OopHandle, mtClassShared>* _pending_roots;
   static GrowableArrayCHeap<OopHandle, mtClassShared>* _root_segments;
+  static GrowableArrayCHeap<oop, mtClassShared>* _trace; // for debugging unarchivable objects
+  static GrowableArrayCHeap<const char*, mtClassShared>* _context; // for debugging unarchivable objects
   static int _root_segment_max_size_elems;
   static OopHandle _scratch_basic_type_mirrors[T_VOID+1];
   static MetaspaceObjToOopHandleTable* _scratch_java_mirror_table;
   static MetaspaceObjToOopHandleTable* _scratch_references_table;
 
@@ -324,10 +327,13 @@
     assert(_seen_objects_table != nullptr, "must be");
     delete _seen_objects_table;
     _seen_objects_table = nullptr;
   }
 
+  class ArchivingObjectMark;
+  class ContextMark;
+
   // Statistics (for one round of start_recording_subgraph ... done_recording_subgraph)
   static int _num_new_walked_objs;
   static int _num_new_archived_objs;
   static int _num_old_recorded_klasses;
 
@@ -377,10 +383,11 @@
   static void find_all_aot_initialized_classes();
   static void find_all_aot_initialized_classes_helper();
   static bool scan_for_aot_initialized_classes(oop obj);
 
  public:
+  static void exit_on_error();
   static void reset_archived_object_states(TRAPS);
   static void create_archived_object_cache() {
     _archived_object_cache =
       new (mtClass)ArchivedObjectCache(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE);
   }
@@ -400,10 +407,12 @@
                                              KlassSubGraphInfo* subgraph_info,
                                              oop orig_obj);
 
   static void add_to_dumped_interned_strings(oop string);
 
+  static void track_scratch_object(oop orig_obj, oop scratch_obj);
+
   // Scratch objects for archiving Klass::java_mirror()
   static void set_scratch_java_mirror(Klass* k, oop mirror);
   static void remove_scratch_objects(Klass* k);
   static void get_pointer_info(oop src_obj, bool& has_oop_pointers, bool& has_native_pointers);
   static void set_has_native_pointers(oop src_obj);
@@ -423,25 +432,24 @@
   // Other types of roots are also cleared similarly when they become referenced.
 
   // Dump-time only. Returns the index of the root, which can be used at run time to read
   // the root using get_root(index, ...).
   static int append_root(oop obj);
-  static GrowableArrayCHeap<oop, mtClassShared>* pending_roots() { return _pending_roots; }
+  static GrowableArrayCHeap<OopHandle, mtClassShared>* pending_roots() { return _pending_roots; }
 
   // Dump-time and runtime
   static objArrayOop root_segment(int segment_idx);
   static oop get_root(int index, bool clear=false);
 
   // Run-time only
   static void clear_root(int index);
-
   static void get_segment_indexes(int index, int& segment_index, int& internal_index);
-
   static void setup_test_class(const char* test_class_name) PRODUCT_RETURN;
 #endif // INCLUDE_CDS_JAVA_HEAP
 
  public:
+  static oop orig_to_scratch_object(oop orig_obj);
   static objArrayOop scratch_resolved_references(ConstantPool* src);
   static void add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) NOT_CDS_JAVA_HEAP_RETURN;
   static void init_scratch_objects(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
   static void init_box_classes(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
   static bool is_heap_region(int idx) {
@@ -461,19 +469,36 @@
 #ifndef PRODUCT
   static bool is_a_test_class_in_unnamed_module(Klass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
   static void initialize_test_class_from_archive(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
 #endif
 
+  static void add_to_permanent_oop_table(oop obj, int offset);
+
+  // AOT-compile time only: get a stable index for an archived object.
+  // Returns 0 if obj is not archived.
+  static int get_archived_object_permanent_index(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(-1);
+  // Runtime only: get back the same object for an index returned by
+  // get_archived_object_permanent_index().
+  static oop get_archived_object(int permanent_index) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
+
   static void initialize_java_lang_invoke(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
   static void init_classes_for_special_subgraph(Handle loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
 
   static bool is_lambda_form_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
   static bool is_lambda_proxy_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
   static bool is_string_concat_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
   static bool is_archivable_hidden_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
 };
 
+class CachedCodeDirectoryInternal {
+  int _permanent_oop_count;
+  int* _permanent_oop_offsets; // offset of each permanent object from the bottom of the archived heap
+public:
+  void dumptime_init_internal();
+  void runtime_init_internal();
+};
+
 #if INCLUDE_CDS_JAVA_HEAP
 class DumpedInternedStrings :
   public ResizeableResourceHashtable<oop, bool,
                            AnyObj::C_HEAP,
                            mtClassShared,
diff a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp
--- a/src/hotspot/share/cds/lambdaFormInvokers.cpp
+++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "cds/archiveBuilder.hpp"
+#include "cds/cdsConfig.hpp"
 #include "cds/lambdaFormInvokers.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "cds/regeneratedClasses.hpp"
 #include "classfile/classFileStream.hpp"
 #include "classfile/classLoadInfo.hpp"
@@ -88,10 +89,13 @@
     log_info(cds)("Regenerate MethodHandle Holder classes...done");
   }
 };
 
 void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
+  if (!CDSConfig::is_dumping_regenerated_lambdaform_invokers()) {
+    return;
+  }
   PrintLambdaFormMessage plm;
   if (_lambdaform_lines == nullptr || _lambdaform_lines->length() == 0) {
     log_info(cds)("Nothing to regenerate for holder classes");
     return;
   }
@@ -180,11 +184,13 @@
       int len = h_bytes->length();
       // make a copy of class bytes so GC will not affect us.
       char *buf = NEW_RESOURCE_ARRAY(char, len);
       memcpy(buf, (char*)h_bytes->byte_at_addr(0), len);
       ClassFileStream st((u1*)buf, len, nullptr);
-      regenerate_class(class_name, st, CHECK);
+      if (!CDSConfig::is_dumping_invokedynamic() /* work around JDK-8310831 */) {
+        regenerate_class(class_name, st, CHECK);
+      }
     }
   }
 }
 
 void LambdaFormInvokers::regenerate_class(char* class_name, ClassFileStream& st, TRAPS) {
@@ -219,10 +225,17 @@
   log_info(cds, lambda)("Regenerated class %s, old: " INTPTR_FORMAT " new: " INTPTR_FORMAT,
                  class_name, p2i(klass), p2i(result));
 }
 
 void LambdaFormInvokers::dump_static_archive_invokers() {
+  if (CDSConfig::is_dumping_preimage_static_archive() ||
+      CDSConfig::is_dumping_final_static_archive()) {
+    // This function writes the "names" of the invokers.
+    // This is not supported in new CDS workflow for now.
+    return;
+  }
+
   if (_lambdaform_lines != nullptr && _lambdaform_lines->length() > 0) {
     int count = 0;
     int len   = _lambdaform_lines->length();
     for (int i = 0; i < len; i++) {
       char* str = _lambdaform_lines->at(i);
diff a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp
--- a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp
+++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp
@@ -191,11 +191,12 @@
   void add_proxy_klass(InstanceKlass* proxy_klass) {
     if (_proxy_klasses == nullptr) {
       _proxy_klasses = new (mtClassShared) GrowableArray<InstanceKlass*>(5, mtClassShared);
     }
     assert(_proxy_klasses != nullptr, "sanity");
-    _proxy_klasses->append(proxy_klass);
+    // Try to preserve the order. get_shared_lambda_proxy_class returns shared classes in reverse order.
+    _proxy_klasses->insert_before(0, proxy_klass);
   }
 
   void metaspace_pointers_do(MetaspaceClosure* it) {
     for (int i=0; i<_proxy_klasses->length(); i++) {
       it->push(_proxy_klasses->adr_at(i));
diff a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp
--- a/src/hotspot/share/cds/metaspaceShared.cpp
+++ b/src/hotspot/share/cds/metaspaceShared.cpp
@@ -21,25 +21,28 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotClassInitializer.hpp"
 #include "cds/aotClassLinker.hpp"
 #include "cds/aotConstantPoolResolver.hpp"
 #include "cds/aotLinkedClassBulkLoader.hpp"
 #include "cds/archiveBuilder.hpp"
 #include "cds/archiveHeapLoader.hpp"
 #include "cds/archiveHeapWriter.hpp"
 #include "cds/cds_globals.hpp"
+#include "cds/cdsAccess.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/cdsProtectionDomain.hpp"
 #include "cds/classListParser.hpp"
 #include "cds/classListWriter.hpp"
 #include "cds/cppVtables.hpp"
 #include "cds/dumpAllocStats.hpp"
 #include "cds/dynamicArchive.hpp"
 #include "cds/filemap.hpp"
+#include "cds/finalImageRecipes.hpp"
 #include "cds/heapShared.hpp"
 #include "cds/lambdaFormInvokers.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/classLoaderDataShared.hpp"
@@ -53,10 +56,13 @@
 #include "classfile/systemDictionary.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "code/codeCache.hpp"
+#include "code/SCCache.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/precompiler.hpp"
 #include "gc/shared/gcVMOperations.hpp"
 #include "interpreter/bytecodeStream.hpp"
 #include "interpreter/bytecodes.hpp"
 #include "jvm_io.h"
 #include "logging/log.hpp"
@@ -68,14 +74,17 @@
 #include "memory/universe.hpp"
 #include "nmt/memTracker.hpp"
 #include "oops/compressedKlass.hpp"
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/method.inline.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oopHandle.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/jvmtiExport.hpp"
+#include "prims/whitebox.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/globals.hpp"
 #include "runtime/globals_extension.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/javaCalls.hpp"
@@ -99,10 +108,11 @@
 void* MetaspaceShared::_shared_metaspace_static_top = nullptr;
 intx MetaspaceShared::_relocation_delta;
 char* MetaspaceShared::_requested_base_address;
 Array<Method*>* MetaspaceShared::_archived_method_handle_intrinsics = nullptr;
 bool MetaspaceShared::_use_optimized_module_handling = true;
+int volatile MetaspaceShared::_preimage_static_archive_dumped = 0;
 
 // The CDS archive is divided into the following regions:
 //     rw  - read-write metadata
 //     ro  - read-only metadata and read-only tables
 //     hp  - heap region
@@ -260,10 +270,18 @@
   return aligned_base;
 }
 
 void MetaspaceShared::initialize_for_static_dump() {
   assert(CDSConfig::is_dumping_static_archive(), "sanity");
+
+  if (CDSConfig::is_dumping_preimage_static_archive() || CDSConfig::is_dumping_final_static_archive()) {
+    if (!((UseG1GC || UseParallelGC || UseSerialGC || UseEpsilonGC || UseShenandoahGC) && UseCompressedClassPointers)) {
+      vm_exit_during_initialization("Cannot create the CacheDataStore",
+                                    "UseCompressedClassPointers must be enabled, and collector must be G1, Parallel, Serial, Epsilon, or Shenandoah");
+    }
+  }
+
   log_info(cds)("Core region alignment: " SIZE_FORMAT, core_region_alignment());
   // The max allowed size for CDS archive. We use this to limit SharedBaseAddress
   // to avoid address space wrap around.
   size_t cds_max;
   const size_t reserve_alignment = core_region_alignment();
@@ -460,10 +478,12 @@
   SymbolTable::serialize_shared_table_header(soc);
   StringTable::serialize_shared_table_header(soc);
   HeapShared::serialize_tables(soc);
   SystemDictionaryShared::serialize_dictionary_headers(soc);
   AOTLinkedClassBulkLoader::serialize(soc, true);
+  FinalImageRecipes::serialize(soc, true);
+  TrainingData::serialize_training_data(soc);
   InstanceMirrorKlass::serialize_offsets(soc);
 
   // Dump/restore well known classes (pointers)
   SystemDictionaryShared::serialize_vm_classes(soc);
   soc->do_tag(--tag);
@@ -544,10 +564,11 @@
   virtual void iterate_roots(MetaspaceClosure* it) {
     FileMapInfo::metaspace_pointers_do(it);
     SystemDictionaryShared::dumptime_classes_do(it);
     Universe::metaspace_pointers_do(it);
     vmSymbols::metaspace_pointers_do(it);
+    TrainingData::iterate_roots(it);
 
     // The above code should find all the symbols that are referenced by the
     // archived classes. We just need to add the extra symbols which
     // may not be used by any of the archived classes -- these are usually
     // symbols that we anticipate to be used at run time, so we can store
@@ -582,10 +603,15 @@
 char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
   ArchiveBuilder::OtherROAllocMark mark;
 
   SystemDictionaryShared::write_to_archive();
   AOTClassLinker::write_to_archive();
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    FinalImageRecipes::record_recipes();
+  }
+  AOTLinkedClassBulkLoader::record_unregistered_classes();
+  TrainingData::dump_training_data();
   MetaspaceShared::write_method_handle_intrinsics();
 
   // Write lambform lines into archive
   LambdaFormInvokers::dump_static_archive_invokers();
 
@@ -597,11 +623,11 @@
 
   return start;
 }
 
 void VM_PopulateDumpSharedSpace::doit() {
-  guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump");
+  //guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump");
 
   DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
 
   _pending_method_handle_intrinsics = new (mtClassShared) GrowableArray<Method*>(256, mtClassShared);
   if (CDSConfig::is_dumping_aot_linked_classes()) {
@@ -638,16 +664,26 @@
   char* early_serialized_data = dump_early_read_only_tables();
   char* serialized_data = dump_read_only_tables();
 
   SystemDictionaryShared::adjust_lambda_proxy_class_dictionary();
 
+  log_info(cds)("Make training data shareable");
+  _builder.make_training_data_shareable();
+
   // The vtable clones contain addresses of the current process.
   // We don't want to write these addresses into the archive.
   CppVtables::zero_archived_vtables();
 
   // Write the archive file
-  const char* static_archive = CDSConfig::static_archive_path();
+  const char* static_archive;
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    static_archive = CacheDataStore;
+    assert(FileMapInfo::current_info() != nullptr, "sanity");
+    delete FileMapInfo::current_info();
+  } else {
+    static_archive = CDSConfig::static_archive_path();
+  }
   assert(static_archive != nullptr, "SharedArchiveFile not set?");
   _map_info = new FileMapInfo(static_archive, true);
   _map_info->populate_header(MetaspaceShared::core_region_alignment());
   _map_info->set_early_serialized_data(early_serialized_data);
   _map_info->set_serialized_data(serialized_data);
@@ -676,16 +712,20 @@
 };
 
 // Check if we can eagerly link this class at dump time, so we can avoid the
 // runtime linking overhead (especially verification)
 bool MetaspaceShared::may_be_eagerly_linked(InstanceKlass* ik) {
+  if (CDSConfig::preserve_all_dumptime_verification_states(ik)) {
+    assert(ik->can_be_verified_at_dumptime(), "sanity");
+  }
   if (!ik->can_be_verified_at_dumptime()) {
     // For old classes, try to leave them in the unlinked state, so
     // we can still store them in the archive. They must be
     // linked/verified at runtime.
     return false;
   }
+
   if (CDSConfig::is_dumping_dynamic_archive() && ik->is_shared_unregistered_class()) {
     // Linking of unregistered classes at this stage may cause more
     // classes to be resolved, resulting in calls to ClassLoader.loadClass()
     // that may not be expected by custom class loaders.
     //
@@ -694,23 +734,21 @@
     return false;
   }
   return true;
 }
 
-bool MetaspaceShared::link_class_for_cds(InstanceKlass* ik, TRAPS) {
-  // Link the class to cause the bytecodes to be rewritten and the
-  // cpcache to be created. Class verification is done according
-  // to -Xverify setting.
-  bool res = MetaspaceShared::try_link_class(THREAD, ik);
-  AOTConstantPoolResolver::dumptime_resolve_constants(ik, CHECK_(false));
-  return res;
-}
 
 void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) {
   AOTClassLinker::initialize();
 
-  if (!jcmd_request) {
+  if (!jcmd_request && !CDSConfig::is_dumping_dynamic_archive()
+      && !CDSConfig::is_dumping_preimage_static_archive()
+      && !CDSConfig::is_dumping_final_static_archive()) {
+    // If we have regenerated invoker classes in the dynamic archive,
+    // they will conflict with the resolved CONSTANT_Klass references that are stored
+    // in the static archive. This is not easy to handle. Let's disable
+    // it for dynamic archive for now.
     LambdaFormInvokers::regenerate_holder_classes(CHECK);
   }
 
   // Collect all loaded ClassLoaderData.
   CollectCLDClosure collect_cld(THREAD);
@@ -729,11 +767,14 @@
       ClassLoaderData* cld = collect_cld.cld_at(i);
       for (Klass* klass = cld->klasses(); klass != nullptr; klass = klass->next_link()) {
         if (klass->is_instance_klass()) {
           InstanceKlass* ik = InstanceKlass::cast(klass);
           if (may_be_eagerly_linked(ik)) {
-            has_linked |= link_class_for_cds(ik, CHECK);
+            has_linked |= try_link_class(THREAD, ik);
+          }
+          if (CDSConfig::is_dumping_heap() && ik->is_linked() && !ik->is_initialized()) {
+            AOTClassInitializer::maybe_preinit_class(ik, CHECK);
           }
         }
       }
     }
 
@@ -741,10 +782,38 @@
       break;
     }
     // Class linking includes verification which may load more classes.
     // Keep scanning until we have linked no more classes.
   }
+
+  // Resolve constant pool entries -- we don't load any new classes during this stage
+  for (int i = 0; i < collect_cld.nof_cld(); i++) {
+    ClassLoaderData* cld = collect_cld.cld_at(i);
+    for (Klass* klass = cld->klasses(); klass != nullptr; klass = klass->next_link()) {
+      if (klass->is_instance_klass()) {
+        InstanceKlass* ik = InstanceKlass::cast(klass);
+        AOTConstantPoolResolver::dumptime_resolve_constants(ik, CHECK);
+        if (CDSConfig::is_dumping_preimage_static_archive()) {
+          FinalImageRecipes::add_reflection_data_flags(ik, CHECK);
+        }
+      }
+    }
+  }
+
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    // Do this after all classes are verified by the above loop.
+    // Any classes loaded from here on will be automatically excluded, so
+    // there's no need to force verification or resolve CP entries.
+    RecordTraining = false;
+    SystemDictionaryShared::ignore_new_classes();
+    LambdaFormInvokers::regenerate_holder_classes(CHECK);
+    RecordTraining = true;
+  }
+
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    FinalImageRecipes::apply_recipes(CHECK);
+  }
 }
 
 void MetaspaceShared::prepare_for_dumping() {
   assert(CDSConfig::is_dumping_archive(), "sanity");
   CDSConfig::check_unsupported_dumping_module_options();
@@ -754,10 +823,17 @@
 // Preload classes from a list, populate the shared spaces and dump to a
 // file.
 void MetaspaceShared::preload_and_dump(TRAPS) {
   CDSConfig::DumperThreadMark dumper_thread_mark(THREAD);
   ResourceMark rm(THREAD);
+  HandleMark hm(THREAD);
+
+  if (CDSConfig::is_dumping_final_static_archive() && PrintTrainingInfo) {
+    tty->print_cr("==================== archived_training_data ** before dumping ====================");
+    TrainingData::print_archived_training_data_on(tty);
+  }
+
   StaticArchiveBuilder builder;
   preload_and_dump_impl(builder, THREAD);
   if (HAS_PENDING_EXCEPTION) {
     if (PENDING_EXCEPTION->is_a(vmClasses::OutOfMemoryError_klass())) {
       log_error(cds)("Out of memory. Please run with a larger Java heap, current MaxHeapSize = "
@@ -768,11 +844,11 @@
                      java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION)));
       MetaspaceShared::writing_error("Unexpected exception, use -Xlog:cds,exceptions=trace for detail");
     }
   }
 
-  if (!CDSConfig::old_cds_flags_used()) {
+  if (!CDSConfig::old_cds_flags_used() && !CDSConfig::is_dumping_preimage_static_archive() && !CDSConfig::is_dumping_final_static_archive()) {
     // The JLI launcher only recognizes the "old" -Xshare:dump flag.
     // When the new -XX:AOTMode=create flag is used, we can't return
     // to the JLI launcher, as the launcher will fail when trying to
     // run the main class, which is not what we want.
     tty->print_cr("AOTCache creation is complete: %s", AOTCache);
@@ -873,17 +949,44 @@
 
   // Exercise FileSystem and URL code
   CDSProtectionDomain::to_file_URL("dummy.jar", Handle(), CHECK);
 }
 
+bool MetaspaceShared::is_recording_preimage_static_archive() {
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+      return _preimage_static_archive_dumped == 0;
+  }
+  return false;
+}
+
 void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS) {
-  preload_classes(CHECK);
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    if (Atomic::cmpxchg(&_preimage_static_archive_dumped, 0, 1) != 0) {
+      return;
+    }
+  }
+
+  if (CDSConfig::is_dumping_classic_static_archive()) {
+    // We are running with -Xshare:dump
+    preload_classes(CHECK);
 
-  if (SharedArchiveConfigFile) {
-    log_info(cds)("Reading extra data from %s ...", SharedArchiveConfigFile);
-    read_extra_data(THREAD, SharedArchiveConfigFile);
-    log_info(cds)("Reading extra data: done.");
+    if (SharedArchiveConfigFile) {
+      log_info(cds)("Reading extra data from %s ...", SharedArchiveConfigFile);
+      read_extra_data(THREAD, SharedArchiveConfigFile);
+      log_info(cds)("Reading extra data: done.");
+    }
+  }
+
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    log_info(cds)("Reading lambda form invokers of in JDK default classlist ...");
+    char default_classlist[JVM_MAXPATHLEN];
+    get_default_classlist(default_classlist, sizeof(default_classlist));
+    struct stat statbuf;
+    if (os::stat(default_classlist, &statbuf) == 0) {
+      ClassListParser::parse_classlist(default_classlist,
+                                       ClassListParser::_parse_lambda_forms_invokers_only, CHECK);
+    }
   }
 
   // Rewrite and link classes
   log_info(cds)("Rewriting and linking classes ...");
 
@@ -892,10 +995,17 @@
   // fails verification, all other interfaces that were not specified in the classlist but
   // are implemented by K are not verified.
   link_shared_classes(false/*not from jcmd*/, CHECK);
   log_info(cds)("Rewriting and linking classes: done");
 
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    assert(RecordTraining == false, "must be");
+    RecordTraining = true;
+  }
+
+  TrainingData::init_dumptime_table(CHECK); // captures TrainingDataSetLocker
+
 #if INCLUDE_CDS_JAVA_HEAP
   if (CDSConfig::is_dumping_heap()) {
     if (!HeapShared::is_archived_boot_layer_available(THREAD)) {
       log_info(cds)("archivedBootLayer not available, disabling full module graph");
       CDSConfig::stop_dumping_full_module_graph();
@@ -904,10 +1014,14 @@
     ArchiveHeapWriter::init();
     if (CDSConfig::is_dumping_full_module_graph()) {
       HeapShared::reset_archived_object_states(CHECK);
     }
 
+    if (ArchiveLoaderLookupCache) {
+      SystemDictionaryShared::create_loader_positive_lookup_cache(CHECK);
+    }
+
     if (CDSConfig::is_dumping_invokedynamic()) {
       // This assert means that the MethodType and MethodTypeForm tables won't be
       // updated concurrently when we are saving their contents into a side table.
       assert(CDSConfig::allow_only_single_java_thread(), "Required");
 
@@ -938,12 +1052,46 @@
   }
 #endif
 
   VM_PopulateDumpSharedSpace op(builder);
   VMThread::execute(&op);
+  FileMapInfo* mapinfo = op.map_info();
+  ArchiveHeapInfo* heap_info = op.heap_info();
+  bool status;
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    if ((status = write_static_archive(&builder, mapinfo, heap_info))) {
+      fork_and_dump_final_static_archive();
+    }
+  } else if (CDSConfig::is_dumping_final_static_archive()) {
+    RecordTraining = false;
+    if (StoreCachedCode && CachedCodeFile != nullptr) { // FIXME: new workflow -- remove the CachedCodeFile flag
+      if (log_is_enabled(Info, cds, jit)) {
+        CDSAccess::test_heap_access_api();
+      }
+
+      // We have just created the final image. Let's run the AOT compiler
+      if (PrintTrainingInfo) {
+        tty->print_cr("==================== archived_training_data ** after dumping ====================");
+        TrainingData::print_archived_training_data_on(tty);
+      }
+
+      CDSConfig::enable_dumping_cached_code();
+      {
+        builder.start_cc_region();
+        Precompiler::compile_cached_code(&builder, CHECK);
+        builder.end_cc_region();
+      }
+      CDSConfig::disable_dumping_cached_code();
+
+      SCCache::close(); // Write final data and close archive
+    }
+    status = write_static_archive(&builder, mapinfo, heap_info);
+  } else {
+    status = write_static_archive(&builder, mapinfo, heap_info);
+  }
 
-  if (!write_static_archive(&builder, op.map_info(), op.heap_info())) {
+  if (!status) {
     THROW_MSG(vmSymbols::java_io_IOException(), "Encountered error while dumping");
   }
 }
 
 bool MetaspaceShared::write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, ArchiveHeapInfo* heap_info) {
@@ -962,16 +1110,106 @@
             "for testing purposes only and should not be used in a production environment");
   }
   return true;
 }
 
+static void print_java_launcher(outputStream* st) {
+  st->print("%s%sbin%sjava", Arguments::get_java_home(), os::file_separator(), os::file_separator());
+}
+
+static void print_vm_arguments(outputStream* st) {
+  const char* cp = Arguments::get_appclasspath();
+  if (cp != nullptr && strlen(cp) > 0 && strcmp(cp, ".") != 0) {
+    st->print(" -cp ");  st->print_raw(cp);
+  }
+  for (int i = 0; i < Arguments::num_jvm_flags(); i++) {
+    st->print(" %s", Arguments::jvm_flags_array()[i]);
+  }
+  for (int i = 0; i < Arguments::num_jvm_args(); i++) {
+    st->print(" %s", Arguments::jvm_args_array()[i]);
+  }
+}
+
+void MetaspaceShared::fork_and_dump_final_static_archive() {
+  assert(CDSConfig::is_dumping_preimage_static_archive(), "sanity");
+
+  ResourceMark rm;
+  stringStream ss;
+  print_java_launcher(&ss);
+  print_vm_arguments(&ss);
+  ss.print(" -XX:CDSPreimage=%s", SharedArchiveFile);
+
+  const char* cmd = ss.freeze();
+  if (CDSManualFinalImage) {
+    tty->print_cr("-XX:+CDSManualFinalImage is specified");
+    tty->print_cr("Please manually execute the following command to create the final CDS image:");
+    tty->print("    "); tty->print_raw_cr(cmd);
+
+    // The following is useful if the dumping was trigger by a script that builds
+    // a complex command-line.
+    tty->print_cr("Note: to recreate the preimage only:");
+    tty->print_cr("    rm -f %s", CacheDataStore);
+    tty->print("    ");
+    print_java_launcher(tty);
+    print_vm_arguments(tty);
+    if (Arguments::java_command() != nullptr) {
+      tty->print(" %s", Arguments::java_command());
+    }
+    tty->cr();
+  } else {
+    // FIXME: space characters are not properly quoated. E.g.,
+    //      java -Dfoo='a b c' HelloWorld
+    log_info(cds)("Launching child process to create final CDS image:");
+    log_info(cds)("    %s", cmd);
+    int status = os::fork_and_exec(cmd);
+    if (status != 0) {
+      log_error(cds)("Child process finished; status = %d", status);
+      log_error(cds)("To reproduce the error");
+      ResourceMark rm;
+      LogStream ls(Log(cds)::error());
+      ls.print("    "); ls.print_raw_cr(cmd);
+
+      // The following is useful if the dumping was trigger by a script that builds
+      // a complex command-line.
+      ls.print_cr("Note: to recreate the preimage only:");
+      ls.print_cr("    rm -f %s", CacheDataStore);
+      ls.print("    ");
+      print_java_launcher(&ls);
+      print_vm_arguments(&ls);
+      ls.print(" -XX:+UnlockDiagnosticVMOptions -XX:+CDSManualFinalImage");
+      if (Arguments::java_command() != nullptr) {
+        ls.print(" %s", Arguments::java_command());
+      }
+      ls.cr();
+
+      vm_direct_exit(status);
+    } else {
+      log_info(cds)("Child process finished; status = %d", status);
+      // On Windows, need WRITE permission to remove the file.
+      WINDOWS_ONLY(chmod(SharedArchiveFile, _S_IREAD | _S_IWRITE));
+      status = remove(SharedArchiveFile);
+      if (status != 0) {
+        log_error(cds)("Failed to remove CDSPreimage file %s", SharedArchiveFile);
+      } else {
+        log_info(cds)("Removed CDSPreimage file %s", SharedArchiveFile);
+      }
+    }
+  }
+}
+
 // Returns true if the class's status has changed.
 bool MetaspaceShared::try_link_class(JavaThread* current, InstanceKlass* ik) {
   ExceptionMark em(current);
   JavaThread* THREAD = current; // For exception macros.
   assert(CDSConfig::is_dumping_archive(), "sanity");
-  if (!ik->is_shared() && ik->is_loaded() && !ik->is_linked() && ik->can_be_verified_at_dumptime() &&
+
+  if (ik->is_shared() && !CDSConfig::is_dumping_final_static_archive()) {
+    assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
+    return false;
+  }
+
+  if (ik->is_loaded() && !ik->is_linked() && ik->can_be_verified_at_dumptime() &&
       !SystemDictionaryShared::has_class_failed_verification(ik)) {
     bool saved = BytecodeVerificationLocal;
     if (ik->is_shared_unregistered_class() && ik->class_loader() == nullptr) {
       // The verification decision is based on BytecodeVerificationRemote
       // for non-system classes. Since we are using the null classloader
@@ -999,15 +1237,17 @@
 }
 
 #if INCLUDE_CDS_JAVA_HEAP
 void VM_PopulateDumpSharedSpace::dump_java_heap_objects(GrowableArray<Klass*>* klasses) {
   if(!HeapShared::can_write()) {
-    log_info(cds)(
+    if (!CDSConfig::is_dumping_preimage_static_archive()) {
+     log_info(cds)(
       "Archived java heap is not supported as UseG1GC "
       "and UseCompressedClassPointers are required."
       "Current settings: UseG1GC=%s, UseCompressedClassPointers=%s.",
       BOOL_TO_STR(UseG1GC), BOOL_TO_STR(UseCompressedClassPointers));
+    }
     return;
   }
   // Find all the interned strings that should be dumped.
   int i;
   for (i = 0; i < klasses->length(); i++) {
@@ -1086,11 +1326,11 @@
 
 void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
   assert(CDSConfig::is_using_archive(), "Must be called when UseSharedSpaces is enabled");
   MapArchiveResult result = MAP_ARCHIVE_OTHER_FAILURE;
 
-  FileMapInfo* static_mapinfo = open_static_archive();
+  FileMapInfo* static_mapinfo = FileMapInfo::current_info();
   FileMapInfo* dynamic_mapinfo = nullptr;
 
   if (static_mapinfo != nullptr) {
     log_info(cds)("Core region alignment: " SIZE_FORMAT, static_mapinfo->core_region_alignment());
     dynamic_mapinfo = open_dynamic_archive();
@@ -1133,10 +1373,14 @@
     log_info(cds)("Unable to map shared spaces");
     if (PrintSharedArchiveAndExit) {
       MetaspaceShared::unrecoverable_loading_error("Unable to use shared archive.");
     } else if (RequireSharedSpaces) {
       MetaspaceShared::unrecoverable_loading_error("Unable to map shared spaces");
+    } else if (CDSConfig::is_dumping_final_static_archive()) {
+      assert(CDSPreimage != nullptr, "must be");
+      log_error(cds)("Unable to map shared spaces for CDSPreimage = %s", CDSPreimage);
+      MetaspaceShared::unrecoverable_loading_error();
     }
   }
 
   // If mapping failed and -XShare:on, the vm should exit
   bool has_failed = false;
@@ -1147,23 +1391,30 @@
   if (dynamic_mapinfo != nullptr && !dynamic_mapinfo->is_mapped()) {
     has_failed = true;
     delete dynamic_mapinfo;
   }
   if (RequireSharedSpaces && has_failed) {
+    // static archive mapped but dynamic archive failed
       MetaspaceShared::unrecoverable_loading_error("Unable to map shared spaces");
   }
 }
 
-FileMapInfo* MetaspaceShared::open_static_archive() {
+// This is called very early at VM start up to get the size of the cached_code region, which
+// is used in CodeCache::initialize_heaps()
+void MetaspaceShared::open_static_archive() {
+  if (!UseSharedSpaces) {
+    return;
+  }
   const char* static_archive = CDSConfig::static_archive_path();
   assert(static_archive != nullptr, "sanity");
   FileMapInfo* mapinfo = new FileMapInfo(static_archive, true);
   if (!mapinfo->initialize()) {
     delete(mapinfo);
-    return nullptr;
+  } else {
+    FileMapRegion* r = mapinfo->region_at(MetaspaceShared::cc);
+    CDSAccess::set_cached_code_size(r->used_aligned());
   }
-  return mapinfo;
 }
 
 FileMapInfo* MetaspaceShared::open_dynamic_archive() {
   if (CDSConfig::is_dumping_dynamic_archive()) {
     return nullptr;
@@ -1584,10 +1835,14 @@
   assert(CDSConfig::is_using_archive(), "must be runtime");
   if (mapinfo == nullptr) {
     return MAP_ARCHIVE_SUCCESS; // The dynamic archive has not been specified. No error has happened -- trivially succeeded.
   }
 
+  if (!mapinfo->validate_aot_class_linking()) {
+    return MAP_ARCHIVE_OTHER_FAILURE;
+  }
+
   mapinfo->set_is_mapped(false);
   if (mapinfo->core_region_alignment() != (size_t)core_region_alignment()) {
     log_info(cds)("Unable to map CDS archive -- core_region_alignment() expected: " SIZE_FORMAT
                   " actual: " SIZE_FORMAT, mapinfo->core_region_alignment(), core_region_alignment());
     return MAP_ARCHIVE_OTHER_FAILURE;
@@ -1661,10 +1916,11 @@
   // Finish up archived heap initialization. These must be
   // done after ReadClosure.
   static_mapinfo->patch_heap_embedded_pointers();
   ArchiveHeapLoader::finish_initialization();
   Universe::load_archived_object_instances();
+  SCCache::new_workflow_load_cache();
 
   // Close the mapinfo file
   static_mapinfo->close();
 
   static_mapinfo->unmap_region(MetaspaceShared::bm);
@@ -1711,10 +1967,16 @@
     if (dynamic_mapinfo != nullptr) {
       tty->print_cr("\n\nDynamic archive name: %s", dynamic_mapinfo->full_path());
       tty->print_cr("Dynamic archive version %d", dynamic_mapinfo->version());
       SystemDictionaryShared::print_shared_archive(tty, false/*dynamic*/);
     }
+    TrainingData::print_archived_training_data_on(tty);
+
+    if (LoadCachedCode) {
+      tty->print_cr("\n\nCached Code file: %s", CachedCodeFile);
+      SCCache::print_on(tty);
+    }
 
     // collect shared symbols and strings
     CountSharedSymbols cl;
     SymbolTable::shared_symbols_do(&cl);
     tty->print_cr("Number of shared symbols: %d", cl.total());
diff a/src/hotspot/share/cds/metaspaceShared.hpp b/src/hotspot/share/cds/metaspaceShared.hpp
--- a/src/hotspot/share/cds/metaspaceShared.hpp
+++ b/src/hotspot/share/cds/metaspaceShared.hpp
@@ -46,10 +46,12 @@
   MAP_ARCHIVE_SUCCESS,
   MAP_ARCHIVE_MMAP_FAILURE,
   MAP_ARCHIVE_OTHER_FAILURE
 };
 
+class StaticArchiveBuilder;
+
 // Class Data Sharing Support
 class MetaspaceShared : AllStatic {
   static ReservedSpace _symbol_rs;  // used only during -Xshare:dump
   static VirtualSpace _symbol_vs;   // used only during -Xshare:dump
   static bool _archive_loading_failed;
@@ -57,20 +59,22 @@
   static void* _shared_metaspace_static_top;
   static intx _relocation_delta;
   static char* _requested_base_address;
   static bool _use_optimized_module_handling;
   static Array<Method*>* _archived_method_handle_intrinsics;
+  static int volatile _preimage_static_archive_dumped;
 
  public:
   enum {
     // core archive spaces
     rw = 0,  // read-write shared space
     ro = 1,  // read-only shared space
     bm = 2,  // relocation bitmaps (freed after file mapping is finished)
     hp = 3,  // heap region
+    cc = 4,  // cached code
     num_core_region = 2,       // rw and ro
-    n_regions = 4              // total number of regions
+    n_regions = 5              // total number of regions
   };
 
   static void prepare_for_dumping() NOT_CDS_RETURN;
   static void preload_and_dump(TRAPS) NOT_CDS_RETURN;
 #ifdef _LP64
@@ -86,10 +90,11 @@
   static Symbol* symbol_rs_base() {
     return (Symbol*)_symbol_rs.base();
   }
 
   static void initialize_for_static_dump() NOT_CDS_RETURN;
+  static void open_static_archive() NOT_CDS_RETURN;
   static void initialize_runtime_shared_and_meta_spaces() NOT_CDS_RETURN;
   static void post_initialize(TRAPS) NOT_CDS_RETURN;
 
   static void print_on(outputStream* st);
 
@@ -108,10 +113,12 @@
   static void set_shared_metaspace_range(void* base, void *static_top, void* top) NOT_CDS_RETURN;
 
   static bool is_shared_dynamic(void* p) NOT_CDS_RETURN_(false);
   static bool is_shared_static(void* p) NOT_CDS_RETURN_(false);
 
+  static bool is_recording_preimage_static_archive() NOT_CDS_RETURN_(false);
+
   static void unrecoverable_loading_error(const char* message = nullptr);
   static void unrecoverable_writing_error(const char* message = nullptr);
   static void writing_error(const char* message = nullptr);
 
   static void make_method_handle_intrinsics_shareable() NOT_CDS_RETURN;
@@ -130,11 +137,10 @@
     NOT_CDS(return false);
   }
 
   static bool try_link_class(JavaThread* current, InstanceKlass* ik);
   static void link_shared_classes(bool jcmd_request, TRAPS) NOT_CDS_RETURN;
-  static bool link_class_for_cds(InstanceKlass* ik, TRAPS) NOT_CDS_RETURN_(false);
   static bool may_be_eagerly_linked(InstanceKlass* ik) NOT_CDS_RETURN_(false);
 
 #if INCLUDE_CDS
   // Alignment for the 2 core CDS regions (RW/RO) only.
   // (Heap region alignments are decided by GC).
@@ -176,12 +182,12 @@
   static bool use_optimized_module_handling() { return NOT_CDS(false) CDS_ONLY(_use_optimized_module_handling); }
   static void disable_optimized_module_handling() { _use_optimized_module_handling = false; }
 
 private:
   static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN;
+  static void fork_and_dump_final_static_archive();
   static bool write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, ArchiveHeapInfo* heap_info);
-  static FileMapInfo* open_static_archive();
   static FileMapInfo* open_dynamic_archive();
   // use_requested_addr: If true (default), attempt to map at the address the
   static MapArchiveResult map_archives(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo,
                                        bool use_requested_addr);
   static char* reserve_address_space_for_archives(FileMapInfo* static_mapinfo,
diff a/src/hotspot/share/cds/methodProfiler.cpp b/src/hotspot/share/cds/methodProfiler.cpp
--- /dev/null
+++ b/src/hotspot/share/cds/methodProfiler.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "cds/methodProfiler.hpp"
+#include "code/codeBlob.hpp"
+#include "code/nmethod.hpp"
+#include "logging/log.hpp"
+#include "runtime/frame.inline.hpp"
+#include "runtime/handshake.hpp"
+#include "runtime/javaThread.hpp"
+#include "runtime/registerMap.hpp"
+#include "runtime/task.hpp"
+
+volatile uint64_t MethodProfiler::_num_samples = 0;
+class MethodProfilerTask : public PeriodicTask {
+public:
+  MethodProfilerTask()
+    : PeriodicTask(RecordOptCompilationOrderInterval) {}
+
+  void task() {
+    MethodProfiler::tick();
+  }
+};
+
+static MethodProfilerTask* _task;
+
+void MethodProfiler::initialize() {
+  if (RecordOptCompilationOrder) {
+    _task = new MethodProfilerTask();
+    _task->enroll();
+  }
+}
+
+class MethodProfilerClosure : public HandshakeClosure {
+public:
+MethodProfilerClosure()
+    : HandshakeClosure("MethodProfiler") {}
+
+  void do_thread(Thread* thread) {
+    ResourceMark rm;
+
+    if (thread != Thread::current()) {
+      // Run by the VM thread - implication is that the target
+      // thread was blocked or in native, i.e. not executing
+      // Java code.
+      return;
+    }
+
+    JavaThread* jt = JavaThread::cast(thread);
+    if (!jt->has_last_Java_frame()) {
+      return;
+    }
+
+    frame fr = jt->last_frame();
+    if (fr.is_safepoint_blob_frame()) {
+      RegisterMap rm(jt,
+                     RegisterMap::UpdateMap::skip,
+                     RegisterMap::ProcessFrames::skip,
+                     RegisterMap::WalkContinuation::skip);
+      fr = fr.sender(&rm);
+    }
+
+    if (!fr.is_compiled_frame()) {
+      return;
+    }
+
+    nmethod* nm = fr.cb()->as_nmethod();
+    if (!nm->is_compiled_by_c2()) {
+      return;
+    }
+
+    log_trace(cds, profiling)("%s sampled %s::%s: " UINT64_FORMAT,
+                              thread->name(),
+                              nm->method()->method_holder()->name()->as_C_string(),
+                              nm->method()->name()->as_C_string(),
+                              nm->method_profiling_count());
+
+    // Found a C2 top frame that was just executing - sample it
+    nm->inc_method_profiling_count();
+    Atomic::inc(&MethodProfiler::_num_samples);
+  }
+};
+
+void MethodProfiler::tick() {
+  MethodProfilerClosure cl;
+  Handshake::execute(&cl);
+}
+
+static int method_hotness_compare(nmethod** a, nmethod** b) {
+  return (*b)->method_profiling_count() - (*a)->method_profiling_count();
+}
+
+
+GrowableArrayCHeap<nmethod*, mtClassShared>* MethodProfiler::sampled_nmethods() {
+  GrowableArrayCHeap<nmethod*, mtClassShared>* nmethods = new GrowableArrayCHeap<nmethod*, mtClassShared>();
+
+  {
+    MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+    NMethodIterator iter(NMethodIterator::not_unloading);
+    while(iter.next()) {
+      nmethod* nm = iter.method();
+      if (nm->is_compiled_by_c2() || nm->is_compiled_by_jvmci()) {
+        nmethods->append(nm);
+      }
+    }
+  }
+
+  nmethods->sort(&method_hotness_compare);
+
+  return nmethods;
+}
+
+double MethodProfiler::hotness(nmethod* nm) {
+  return double(nm->method_profiling_count()) / double(_num_samples) * 100.0;
+}
+
+uint64_t MethodProfiler::num_samples() {
+  return _num_samples;
+}
+
+void MethodProfiler::process_method_hotness() {
+  if (_num_samples == 0) {
+    return;
+  }
+
+  _task->disenroll();
+
+  LogTarget(Debug, cds, profiling) lt;
+  if (lt.is_enabled()) {
+    ResourceMark rm;
+    double accumulated_sample_percent = 0.0;
+    int i = 0;
+    GrowableArrayCHeap<nmethod*, mtClassShared>* nmethods = sampled_nmethods();
+
+    for (auto it = nmethods->begin(); it != nmethods->end(); ++it) {
+      ++i;
+      nmethod* nm = *it;
+      if (nm->method_profiling_count() == 0) {
+        break;
+      }
+      double sample_percent = hotness(nm);
+      accumulated_sample_percent += sample_percent;
+      log_info(cds, profiling)("%d (%.2f). %s::%s: " UINT64_FORMAT " (%.2f%%, %.2f%% accumulated)",
+                               i,
+                               double(i) / double(nmethods->length()) * 100.0,
+                               nm->method()->method_holder()->name()->as_C_string(),
+                               nm->method()->name()->as_C_string(),
+                               nm->method_profiling_count(),
+                               sample_percent,
+                               accumulated_sample_percent);
+    }
+
+    delete nmethods;
+  }
+}
diff a/src/hotspot/share/cds/methodProfiler.hpp b/src/hotspot/share/cds/methodProfiler.hpp
--- /dev/null
+++ b/src/hotspot/share/cds/methodProfiler.hpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#ifndef SHARE_CDS_METHOD_PROFILER_HPP
+#define SHARE_CDS_METHOD_PROFILER_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/growableArray.hpp"
+
+class nmethod;
+
+class MethodProfiler {
+  friend class MethodProfilerClosure;
+
+private:
+  static volatile uint64_t _num_samples;
+
+public:
+  static void initialize();
+  static void tick();
+
+  static void process_method_hotness();
+
+  static uint64_t num_samples();
+  static GrowableArrayCHeap<nmethod*, mtClassShared>* sampled_nmethods();
+  static double hotness(nmethod* nm);
+};
+
+#endif // SHARE_CDS_METHOD_PROFILER_HPP
diff a/src/hotspot/share/cds/regeneratedClasses.cpp b/src/hotspot/share/cds/regeneratedClasses.cpp
--- a/src/hotspot/share/cds/regeneratedClasses.cpp
+++ b/src/hotspot/share/cds/regeneratedClasses.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024, 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.
@@ -62,20 +62,33 @@
                        p2i(orig_m), orig_m->external_name());
     } else {
       _renegerated_objs->put((address)orig_m, (address)regen_m);
     }
   }
+
+  if (log_is_enabled(Info, cds)) {
+    ResourceMark rm;
+    log_info(cds)("Regenerated class %s: methods %d -> %d)", orig_klass->external_name(),
+                  orig_klass->methods()->length(), regen_klass->methods()->length());
+  }
 }
 
 bool RegeneratedClasses::has_been_regenerated(address orig_obj) {
   if (_renegerated_objs == nullptr) {
     return false;
   } else {
     return _renegerated_objs->get(orig_obj) != nullptr;
   }
 }
 
+address RegeneratedClasses::get_regenerated_object(address orig_obj) {
+  assert(_renegerated_objs != nullptr, "must be");
+  address* p =_renegerated_objs->get(orig_obj);
+  assert(p != nullptr, "must be");
+  return *p;
+}
+
 void RegeneratedClasses::record_regenerated_objects() {
   assert_locked_or_safepoint(DumpTimeTable_lock);
   if (_renegerated_objs != nullptr) {
     auto doit = [&] (address orig_obj, address regen_obj) {
       ArchiveBuilder::current()->record_regenerated_object(orig_obj, regen_obj);
diff a/src/hotspot/share/cds/regeneratedClasses.hpp b/src/hotspot/share/cds/regeneratedClasses.hpp
--- a/src/hotspot/share/cds/regeneratedClasses.hpp
+++ b/src/hotspot/share/cds/regeneratedClasses.hpp
@@ -41,9 +41,10 @@
 class RegeneratedClasses : public AllStatic {
  public:
   static void add_class(InstanceKlass* orig_klass, InstanceKlass* regen_klass);
   static void cleanup();
   static bool has_been_regenerated(address orig_obj);
+  static address get_regenerated_object(address orig_obj);
   static void record_regenerated_objects();
 };
 
 #endif // SHARE_CDS_REGENERATEDCLASSES_HPP
diff a/src/hotspot/share/ci/ciConstant.cpp b/src/hotspot/share/ci/ciConstant.cpp
--- a/src/hotspot/share/ci/ciConstant.cpp
+++ b/src/hotspot/share/ci/ciConstant.cpp
@@ -59,10 +59,23 @@
     }
   }
   return false;
 }
 
+// ------------------------------------------------------------------
+// ciConstant::should_be_constant
+bool ciConstant::should_be_constant() const {
+  if (is_valid()) {
+    if (is_reference_type(basic_type())) {
+      return as_object()->should_be_constant();
+    } else {
+      return true;
+    }
+  }
+  return false;
+}
+
 // ------------------------------------------------------------------
 // ciConstant::print
 void ciConstant::print() {
   tty->print("<ciConstant type=%s value=",
              basictype_to_str(basic_type()));
diff a/src/hotspot/share/ci/ciConstant.hpp b/src/hotspot/share/ci/ciConstant.hpp
--- a/src/hotspot/share/ci/ciConstant.hpp
+++ b/src/hotspot/share/ci/ciConstant.hpp
@@ -116,10 +116,12 @@
     return basic_type() != T_ILLEGAL;
   }
 
   bool is_loaded() const;
 
+  bool should_be_constant() const;
+
   // Debugging output
   void print();
 };
 
 #endif // SHARE_CI_CICONSTANT_HPP
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
+  }
+}
diff a/src/hotspot/share/ci/ciEnv.hpp b/src/hotspot/share/ci/ciEnv.hpp
--- a/src/hotspot/share/ci/ciEnv.hpp
+++ b/src/hotspot/share/ci/ciEnv.hpp
@@ -37,10 +37,11 @@
 #include "oops/methodData.hpp"
 #include "runtime/javaThread.hpp"
 
 class CompileTask;
 class OopMapSet;
+class SCCEntry;
 
 // ciEnv
 //
 // This class is the top level broker for requests from the compiler
 // to the VM.
@@ -372,15 +373,19 @@
                        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*                 entry = nullptr);
 
   // Access to certain well known ciObjects.
 #define VM_CLASS_FUNC(name, ignore_s) \
   ciInstanceKlass* name() { \
     return _##name;\
@@ -466,10 +471,17 @@
   void record_out_of_memory_failure();
 
   // RedefineClasses support
   void metadata_do(MetadataClosure* f) { _factory->metadata_do(f); }
 
+private:
+  SCCEntry* _scc_clinit_barriers_entry;
+
+public:
+  void  set_scc_clinit_barriers_entry(SCCEntry* entry) { _scc_clinit_barriers_entry = entry; }
+  SCCEntry* scc_clinit_barriers_entry()          const { return _scc_clinit_barriers_entry; }
+
   // Replay support
 private:
   static int klass_compare(const InstanceKlass* const &ik1, const InstanceKlass* const &ik2) {
     if (ik1 > ik2) {
       return 1;
@@ -510,8 +522,12 @@
   void record_call_site_obj(Thread* thread, oop obj);
   void record_call_site_method(Thread* thread, Method* adapter);
   void process_invokedynamic(const constantPoolHandle &cp, int index, JavaThread* thread);
   void process_invokehandle(const constantPoolHandle &cp, int index, JavaThread* thread);
   void find_dynamic_call_sites();
+
+  bool is_precompiled();
+  bool is_fully_initialized(InstanceKlass* ik);
+  InstanceKlass::ClassState compute_init_state_for_precompiled(InstanceKlass* ik);
 };
 
 #endif // SHARE_CI_CIENV_HPP
diff a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp
--- a/src/hotspot/share/ci/ciField.cpp
+++ b/src/hotspot/share/ci/ciField.cpp
@@ -27,10 +27,11 @@
 #include "ci/ciInstanceKlass.hpp"
 #include "ci/ciSymbols.hpp"
 #include "ci/ciUtilities.inline.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/vmClasses.hpp"
+#include "code/SCCache.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
@@ -315,10 +316,13 @@
     _constant_value = mirror->field_value_impl(type()->basic_type(), offset_in_bytes());
   }
   if (FoldStableValues && is_stable() && _constant_value.is_null_or_zero()) {
     return ciConstant();
   }
+  if (!SCCache::allow_const_field(_constant_value)) {
+    return ciConstant();
+  }
   return _constant_value;
 }
 
 // ------------------------------------------------------------------
 // ciField::constant_value_of
@@ -328,10 +332,13 @@
   assert(object->is_instance(), "must be instance");
   ciConstant field_value = object->as_instance()->field_value(this);
   if (FoldStableValues && is_stable() && field_value.is_null_or_zero()) {
     return ciConstant();
   }
+  if (!SCCache::allow_const_field(field_value)) {
+    return ciConstant();
+  }
   return field_value;
 }
 
 // ------------------------------------------------------------------
 // ciField::compute_type
diff a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp
@@ -52,18 +52,26 @@
 // Loaded instance klass.
 ciInstanceKlass::ciInstanceKlass(Klass* k) :
   ciKlass(k)
 {
   assert(get_Klass()->is_instance_klass(), "wrong type");
-  assert(get_instanceKlass()->is_loaded(), "must be at least loaded");
+
   InstanceKlass* ik = get_instanceKlass();
 
+#ifdef ASSERT
+  if (!ik->is_loaded()) {
+    ResourceMark rm;
+    ik->print_on(tty);
+    assert(false, "must be at least loaded: %s", ik->name()->as_C_string());
+  }
+#endif // ASSERT
+
   AccessFlags access_flags = ik->access_flags();
   _flags = ciFlags(access_flags);
   _has_finalizer = ik->has_finalizer();
   _has_subklass = flags().is_final() ? subklass_false : subklass_unknown;
-  _init_state = ik->init_state();
+  _init_state = compute_init_state(ik); // _init_state
   _has_nonstatic_fields = ik->has_nonstatic_fields();
   _has_nonstatic_concrete_methods = ik->has_nonstatic_concrete_methods();
   _is_hidden = ik->is_hidden();
   _is_record = ik->is_record();
   _nonstatic_fields = nullptr; // initialized lazily by compute_nonstatic_fields:
@@ -143,14 +151,24 @@
 // ------------------------------------------------------------------
 // ciInstanceKlass::compute_shared_is_initialized
 void ciInstanceKlass::compute_shared_init_state() {
   GUARDED_VM_ENTRY(
     InstanceKlass* ik = get_instanceKlass();
-    _init_state = ik->init_state();
+    _init_state = compute_init_state(ik);
   )
 }
 
+InstanceKlass::ClassState ciInstanceKlass::compute_init_state(InstanceKlass* ik) {
+  ASSERT_IN_VM;
+  ciEnv* env = CURRENT_ENV;
+  if (env != nullptr && env->is_precompiled()) {
+    return env->compute_init_state_for_precompiled(ik);
+  } else {
+    return ik->init_state();
+  }
+}
+
 // ------------------------------------------------------------------
 // ciInstanceKlass::compute_shared_has_subklass
 bool ciInstanceKlass::compute_shared_has_subklass() {
   GUARDED_VM_ENTRY(
     InstanceKlass* ik = get_instanceKlass();
@@ -335,15 +353,15 @@
               bool_to_str(is_initialized()),
               bool_to_str(has_finalizer()),
               bool_to_str(has_subklass()),
               layout_helper());
 
-    _flags.print_klass_flags();
+    _flags.print_klass_flags(st);
 
     if (_super) {
       st->print(" super=");
-      _super->print_name();
+      _super->print_name_on(st);
     }
     if (_java_mirror) {
       st->print(" mirror=PRESENT");
     }
   }
diff a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp
--- a/src/hotspot/share/ci/ciInstanceKlass.hpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.hpp
@@ -42,10 +42,11 @@
   friend class ciEnv;
   friend class ciExceptionHandler;
   friend class ciMethod;
   friend class ciField;
   friend class ciReplay;
+  friend class CompileTrainingData;
 
 private:
   enum SubklassValue { subklass_unknown, subklass_false, subklass_true };
 
   jobject                _loader;
@@ -117,10 +118,12 @@
     if (_is_shared && _init_state != expected) {
       if (is_loaded()) compute_shared_init_state();
     }
   }
 
+  static InstanceKlass::ClassState compute_init_state(InstanceKlass* ik);
+
 public:
   // Has this klass been initialized?
   bool                   is_initialized() {
     update_if_shared(InstanceKlass::fully_initialized);
     return _init_state == InstanceKlass::fully_initialized;
diff a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp
--- a/src/hotspot/share/ci/ciMethod.cpp
+++ b/src/hotspot/share/ci/ciMethod.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotLinkedClassBulkLoader.hpp"
 #include "ci/ciCallProfile.hpp"
 #include "ci/ciExceptionHandler.hpp"
 #include "ci/ciInstanceKlass.hpp"
 #include "ci/ciMethod.hpp"
 #include "ci/ciMethodBlocks.hpp"
@@ -32,10 +33,11 @@
 #include "ci/ciStreams.hpp"
 #include "ci/ciSymbol.hpp"
 #include "ci/ciReplay.hpp"
 #include "ci/ciSymbols.hpp"
 #include "ci/ciUtilities.inline.hpp"
+#include "compiler/compileTask.hpp"
 #include "compiler/abstractCompiler.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/compilerOracle.hpp"
 #include "compiler/methodLiveness.hpp"
 #include "interpreter/interpreter.hpp"
@@ -46,10 +48,11 @@
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/generateOopMap.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/handles.inline.hpp"
 #include "utilities/bitMap.inline.hpp"
 #include "utilities/xmlstream.hpp"
@@ -141,10 +144,11 @@
   _name = env->get_symbol(h_m->name());
   ciSymbol* sig_symbol = env->get_symbol(h_m->signature());
   constantPoolHandle cpool(Thread::current(), h_m->constants());
   _signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol);
   _method_data = nullptr;
+  _method_data_recorded = nullptr;
   // Take a snapshot of these values, so they will be commensurate with the MDO.
   if (ProfileInterpreter || CompilerConfig::is_c1_profiling()) {
     int invcnt = h_m->interpreter_invocation_count();
     // if the value overflowed report it as max int
     _interpreter_invocation_count = invcnt < 0 ? max_jint : invcnt ;
@@ -157,10 +161,26 @@
     _interpreter_invocation_count = 1;
   _inline_instructions_size = -1;
   if (ReplayCompiles) {
     ciReplay::initialize(this);
   }
+  DirectiveSet* directives = DirectivesStack::getMatchingDirective(h_m, CURRENT_ENV->task()->compiler());
+  ccstrlist bci_list = directives->TooManyTrapsAtBCIOption;
+  int len = (int)strlen(bci_list);
+  Arena* arena = CURRENT_ENV->arena();
+  _has_trap_at_bci = new (arena) GrowableArray<int>(arena, 2, 0, 0);
+  for (int i = 0; i < len; i++) {
+    int v = -1;
+    int read;
+    if (sscanf(bci_list + i, "%i%n", &v, &read) != 1) {
+      warning("wrong format for TooManyTrapsAtBCI option: \"%s\"", bci_list);
+      break;
+    }
+    assert(v >= 0 && v < (1<<16), "%i", v);
+    _has_trap_at_bci->append_if_missing(v);
+    i += read;
+  }
 }
 
 
 // ------------------------------------------------------------------
 // ciMethod::ciMethod
@@ -172,15 +192,17 @@
                    ciInstanceKlass* accessor) :
   ciMetadata((Metadata*)nullptr),
   _name(                   name),
   _holder(                 holder),
   _method_data(            nullptr),
+  _method_data_recorded(   nullptr),
   _method_blocks(          nullptr),
   _intrinsic_id(           vmIntrinsics::_none),
   _inline_instructions_size(-1),
   _can_be_statically_bound(false),
   _can_omit_stack_trace(true),
+  _has_trap_at_bci(        nullptr),
   _liveness(               nullptr)
 #if defined(COMPILER2)
   ,
   _flow(                   nullptr),
   _bcea(                   nullptr)
@@ -998,19 +1020,23 @@
 // ------------------------------------------------------------------
 // ciMethod::ensure_method_data
 //
 // Generate new MethodData* objects at compile time.
 // Return true if allocation was successful or no MDO is required.
-bool ciMethod::ensure_method_data(const methodHandle& h_m) {
+bool ciMethod::ensure_method_data(const methodHandle& h_m, bool training_data_only) {
   EXCEPTION_CONTEXT;
   if (is_native() || is_abstract() || h_m()->is_accessor()) {
     return true;
   }
   if (h_m()->method_data() == nullptr) {
-    Method::build_profiling_method_data(h_m, THREAD);
-    if (HAS_PENDING_EXCEPTION) {
-      CLEAR_PENDING_EXCEPTION;
+    if (training_data_only) {
+      Method::install_training_method_data(h_m);
+    } else {
+      Method::build_profiling_method_data(h_m, THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        CLEAR_PENDING_EXCEPTION;
+      }
     }
   }
   if (h_m()->method_data() != nullptr) {
     _method_data = CURRENT_ENV->get_method_data(h_m()->method_data());
     return _method_data->load_data();
@@ -1019,42 +1045,75 @@
     return false;
   }
 }
 
 // public, retroactive version
-bool ciMethod::ensure_method_data() {
+bool ciMethod::ensure_method_data(bool training_data_only) {
   bool result = true;
   if (_method_data == nullptr || _method_data->is_empty()) {
     GUARDED_VM_ENTRY({
       methodHandle mh(Thread::current(), get_Method());
-      result = ensure_method_data(mh);
+      result = ensure_method_data(mh, training_data_only);
     });
   }
   return result;
 }
 
 
 // ------------------------------------------------------------------
 // ciMethod::method_data
 //
 ciMethodData* ciMethod::method_data() {
-  if (_method_data != nullptr) {
-    return _method_data;
-  }
-  VM_ENTRY_MARK;
-  ciEnv* env = CURRENT_ENV;
-  Thread* my_thread = JavaThread::current();
-  methodHandle h_m(my_thread, get_Method());
-
-  if (h_m()->method_data() != nullptr) {
-    _method_data = CURRENT_ENV->get_method_data(h_m()->method_data());
-    _method_data->load_data();
+  if (CURRENT_ENV->task()->is_precompiled() && CURRENT_ENV->task()->comp_level() == CompLevel_full_optimization) {
+    if (_method_data_recorded == nullptr) {
+      VM_ENTRY_MARK;
+      methodHandle h_m(thread, get_Method());
+      MethodTrainingData* mtd = TrainingData::lookup_for(h_m());
+      MethodData* mdo = (mtd != nullptr ? mtd->final_profile() : nullptr);
+      DirectiveSet* directives = DirectivesStack::getMatchingDirective(h_m, CURRENT_ENV->task()->compiler());
+      if (mdo == nullptr || directives->IgnoreRecordedProfileOption) {
+        if (directives->IgnoreRecordedProfileOption) {
+          ResourceMark rm;
+          log_debug(precompile)("Ignore recorded profile for %s", h_m->name_and_sig_as_C_string());
+        } else {
+          ResourceMark rm;
+          log_debug(precompile)("No profile for %s", h_m->name_and_sig_as_C_string());
+        }
+        _method_data_recorded = CURRENT_ENV->get_empty_methodData();
+      } else {
+#if INCLUDE_CDS
+        if (mdo->extra_data_lock() == nullptr) {
+          assert(!HAS_PENDING_EXCEPTION, "");
+          mdo->restore_unshareable_info(thread);
+          assert(!HAS_PENDING_EXCEPTION, "");
+        }
+#endif
+        _method_data_recorded = CURRENT_ENV->get_method_data(mdo);
+        _method_data_recorded->load_data();
+        {
+          ResourceMark rm;
+          log_debug(precompile)("Recorded profile " PTR_FORMAT " for %s", p2i(mdo), h_m->name_and_sig_as_C_string());
+        }
+      }
+    }
+    assert(_method_data_recorded != nullptr, "");
+    return _method_data_recorded;
   } else {
-    _method_data = CURRENT_ENV->get_empty_methodData();
+    if (_method_data != nullptr) {
+      return _method_data;
+    }
+    VM_ENTRY_MARK;
+    methodHandle h_m(thread, get_Method());
+    MethodData* mdo = h_m()->method_data();
+    if (mdo != nullptr) {
+      _method_data = CURRENT_ENV->get_method_data(mdo);
+      _method_data->load_data();
+    } else {
+      _method_data = CURRENT_ENV->get_empty_methodData();
+    }
+    return _method_data;
   }
-  return _method_data;
-
 }
 
 // ------------------------------------------------------------------
 // ciMethod::method_data_or_null
 // Returns a pointer to ciMethodData if MDO exists on the VM side,
@@ -1105,10 +1164,17 @@
   check_is_loaded();
   ciEnv* env = CURRENT_ENV;
   if (is_c1_compile(env->comp_level())) {
     return _is_c1_compilable;
   }
+
+#if INCLUDE_JVMCI
+  if (EnableJVMCI && UseJVMCICompiler &&
+      env->comp_level() == CompLevel_full_optimization && !AOTLinkedClassBulkLoader::class_preloading_finished()) {
+    return false;
+  }
+#endif
   return _is_c2_compilable;
 }
 
 // ------------------------------------------------------------------
 // ciMethod::has_compiled_code
@@ -1144,19 +1210,49 @@
 // not highly relevant to an inlined method.  So we use the more
 // specific accessor nmethod::insts_size.
 // Also some instructions inside the code are excluded from inline
 // heuristic (e.g. post call nop instructions; see InlineSkippedInstructionsCounter)
 int ciMethod::inline_instructions_size() {
+  if (_inline_instructions_size == -1) {
+    if (TrainingData::have_data()) {
+      GUARDED_VM_ENTRY(
+        CompLevel level = static_cast<CompLevel>(CURRENT_ENV->comp_level());
+        methodHandle top_level_mh(Thread::current(), CURRENT_ENV->task()->method());
+        MethodTrainingData* mtd = MethodTrainingData::find(top_level_mh);
+        if (mtd != nullptr) {
+          CompileTrainingData* ctd = mtd->last_toplevel_compile(level);
+          if (ctd != nullptr) {
+            methodHandle mh(Thread::current(), get_Method());
+            MethodTrainingData* this_mtd = MethodTrainingData::find(mh);
+            if (this_mtd != nullptr) {
+              auto r = ctd->ci_records().ciMethod__inline_instructions_size.find(this_mtd);
+              if (r.is_valid()) {
+                _inline_instructions_size = r.result();
+              }
+            }
+          }
+        }
+      );
+    }
+  }
   if (_inline_instructions_size == -1) {
     GUARDED_VM_ENTRY(
       nmethod* code = get_Method()->code();
-      if (code != nullptr && (code->comp_level() == CompLevel_full_optimization)) {
+      if (code != nullptr && !code->is_scc() && (code->comp_level() == CompLevel_full_optimization)) {
         int isize = code->insts_end() - code->verified_entry_point() - code->skipped_instructions_size();
         _inline_instructions_size = isize > 0 ? isize : 0;
       } else {
         _inline_instructions_size = 0;
       }
+      if (TrainingData::need_data()) {
+        CompileTrainingData* ctd = CURRENT_ENV->task()->training_data();
+        if (ctd != nullptr) {
+          methodHandle mh(Thread::current(), get_Method());
+          MethodTrainingData* this_mtd = MethodTrainingData::make(mh);
+          ctd->ci_records().ciMethod__inline_instructions_size.append_if_missing(_inline_instructions_size, this_mtd);
+        }
+      }
     );
   }
   return _inline_instructions_size;
 }
 
@@ -1181,10 +1277,21 @@
 }
 
 // ------------------------------------------------------------------
 // ciMethod::was_never_executed
 bool ciMethod::was_executed_more_than(int times) {
+  // Invocation counter is reset when the Method* is compiled.
+  // If the method has compiled code we therefore assume it has
+  // be executed more than n times.
+  if (is_accessor() || is_empty() || has_compiled_code()) {
+    // interpreter doesn't bump invocation counter of trivial methods
+    // compiler does not bump invocation counter of compiled methods
+    return true;
+  }
+  if (!method_data()->is_empty()) {
+    return (method_data()->invocation_count() > times);
+  }
   VM_ENTRY_MARK;
   return get_Method()->was_executed_more_than(times);
 }
 
 // ------------------------------------------------------------------
diff a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp
--- a/src/hotspot/share/ci/ciMethod.hpp
+++ b/src/hotspot/share/ci/ciMethod.hpp
@@ -70,10 +70,11 @@
   ciFlags          _flags;
   ciSymbol*        _name;
   ciInstanceKlass* _holder;
   ciSignature*     _signature;
   ciMethodData*    _method_data;
+  ciMethodData*    _method_data_recorded;
   ciMethodBlocks*   _method_blocks;
 
   // Code attributes.
   int _code_size;
   int _max_stack;
@@ -93,10 +94,12 @@
   bool _can_be_statically_bound;
   bool _can_omit_stack_trace;
   bool _has_reserved_stack_access;
   bool _is_overpass;
 
+  GrowableArray<int>* _has_trap_at_bci;
+
   // Lazy fields, filled in on demand
   address              _code;
   ciExceptionHandler** _exception_handlers;
 
   // Optional liveness analyzer.
@@ -115,11 +118,11 @@
 
   void print_impl(outputStream* st);
 
   void load_code();
 
-  bool ensure_method_data(const methodHandle& h_m);
+  bool ensure_method_data(const methodHandle& h_m, bool training_data_only);
 
   void code_at_put(int bci, Bytecodes::Code code) {
     Bytecodes::check(code);
     assert(0 <= bci && bci < code_size(), "valid bci");
     address bcp = _code + bci;
@@ -192,17 +195,19 @@
   int size_of_parameters() const                 { check_is_loaded(); return _size_of_parameters; }
 
   // Code size for inlining decisions.
   int code_size_for_inlining();
 
-  bool caller_sensitive()       const { return get_Method()->caller_sensitive();       }
-  bool force_inline()           const { return get_Method()->force_inline();           }
-  bool dont_inline()            const { return get_Method()->dont_inline();            }
-  bool intrinsic_candidate()    const { return get_Method()->intrinsic_candidate();    }
-  bool is_static_initializer()  const { return get_Method()->is_static_initializer();  }
-  bool changes_current_thread() const { return get_Method()->changes_current_thread(); }
-  bool deprecated()             const { return is_loaded() && get_Method()->deprecated(); }
+  bool caller_sensitive()           const { return get_Method()->caller_sensitive();           }
+  bool force_inline()               const { return get_Method()->force_inline();               }
+  bool dont_inline()                const { return get_Method()->dont_inline();                }
+  bool intrinsic_candidate()        const { return get_Method()->intrinsic_candidate();        }
+  bool is_static_initializer()      const { return get_Method()->is_static_initializer();      }
+  bool changes_current_thread()     const { return get_Method()->changes_current_thread();     }
+  bool deprecated()                 const { return is_loaded() && get_Method()->deprecated();  }
+  bool has_upcall_on_method_entry() const { return get_Method()->has_upcall_on_method_entry(); }
+  bool has_upcall_on_method_exit()  const { return get_Method()->has_upcall_on_method_exit();  }
 
   bool check_intrinsic_candidate() const {
     if (intrinsic_id() == vmIntrinsics::_blackhole) {
       // This is the intrinsic without an associated method, so no intrinsic_candidate
       // flag is set. The intrinsic is still correct.
@@ -312,11 +317,11 @@
   bool is_not_reached(int bci);
   bool was_executed_more_than(int times);
   bool has_unloaded_classes_in_signature();
   bool is_klass_loaded(int refinfo_index, Bytecodes::Code bc, bool must_be_resolved) const;
   bool check_call(int refinfo_index, bool is_static) const;
-  bool ensure_method_data();  // make sure it exists in the VM also
+  bool ensure_method_data(bool training_data_only = false);  // make sure it exists in the VM also
   MethodCounters* ensure_method_counters();
 
   int inline_instructions_size();
   int scale_count(int count, float prof_factor = 1.);  // make MDO count commensurate with IIC
 
@@ -382,8 +387,12 @@
   // Print the name of this method in various incarnations.
   void print_name(outputStream* st = tty);
   void print_short_name(outputStream* st = tty);
 
   static bool is_consistent_info(ciMethod* declared_method, ciMethod* resolved_method);
+  bool has_trap_at(int bci) {
+    return _has_trap_at_bci != nullptr &&
+           _has_trap_at_bci->contains(bci);
+  }
 };
 
 #endif // SHARE_CI_CIMETHOD_HPP
diff a/src/hotspot/share/ci/ciMethodData.cpp b/src/hotspot/share/ci/ciMethodData.cpp
--- a/src/hotspot/share/ci/ciMethodData.cpp
+++ b/src/hotspot/share/ci/ciMethodData.cpp
@@ -67,11 +67,15 @@
       _safepoint_tracker(SafepointSynchronize::safepoint_state_tracker()),
       _uncached_methods()
   { }
 
   bool is_live(Method* m) {
-    if (!m->method_holder()->is_loader_alive()) {
+    Klass* holder = m->method_holder();
+    if (holder == nullptr || // premain: not yet loaded
+        holder->class_loader_data() == nullptr ||
+        !holder->is_loader_alive() ||
+        (holder->is_instance_klass() && !InstanceKlass::cast(holder)->is_loaded())) {
       return false;
     }
     if (CURRENT_ENV->cached_metadata(m) == nullptr) {
       // Uncached entries need to be pre-populated.
       _uncached_methods.append(m);
@@ -302,11 +306,12 @@
 }
 
 void ciReceiverTypeData::translate_receiver_data_from(const ProfileData* data) {
   for (uint row = 0; row < row_limit(); row++) {
     Klass* k = data->as_ReceiverTypeData()->receiver(row);
-    if (k != nullptr) {
+    if (k != nullptr && k->class_loader_data() != nullptr &&
+        (!k->is_instance_klass() || InstanceKlass::cast(k)->is_loaded())) {
       if (k->is_loader_alive()) {
         ciKlass* klass = CURRENT_ENV->get_klass(k);
         set_receiver(row, klass);
       } else {
         // With concurrent class unloading, the MDO could have stale metadata; override it
@@ -320,11 +325,13 @@
 
 void ciTypeStackSlotEntries::translate_type_data_from(const TypeStackSlotEntries* entries) {
   for (int i = 0; i < number_of_entries(); i++) {
     intptr_t k = entries->type(i);
     Klass* klass = (Klass*)klass_part(k);
-    if (klass != nullptr && !klass->is_loader_alive()) {
+    if (klass != nullptr &&
+        ((klass->is_instance_klass() && !InstanceKlass::cast(klass)->is_loaded()) ||
+         (klass->class_loader_data() == nullptr || !klass->is_loader_alive()))) {
       // With concurrent class unloading, the MDO could have stale metadata; override it
       TypeStackSlotEntries::set_type(i, TypeStackSlotEntries::with_status((Klass*)nullptr, k));
     } else {
       TypeStackSlotEntries::set_type(i, translate_klass(k));
     }
@@ -332,11 +339,13 @@
 }
 
 void ciReturnTypeEntry::translate_type_data_from(const ReturnTypeEntry* ret) {
   intptr_t k = ret->type();
   Klass* klass = (Klass*)klass_part(k);
-  if (klass != nullptr && !klass->is_loader_alive()) {
+  if (klass != nullptr &&
+      ((klass->is_instance_klass() && !InstanceKlass::cast(klass)->is_loaded()) ||
+       (klass->class_loader_data() == nullptr || !klass->is_loader_alive()))) {
     // With concurrent class unloading, the MDO could have stale metadata; override it
     set_type(ReturnTypeEntry::with_status((Klass*)nullptr, k));
   } else {
     set_type(translate_klass(k));
   }
diff a/src/hotspot/share/ci/ciObject.cpp b/src/hotspot/share/ci/ciObject.cpp
--- a/src/hotspot/share/ci/ciObject.cpp
+++ b/src/hotspot/share/ci/ciObject.cpp
@@ -23,10 +23,11 @@
  */
 
 #include "precompiled.hpp"
 #include "ci/ciObject.hpp"
 #include "ci/ciUtilities.inline.hpp"
+#include "code/SCCache.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/jniHandles.inline.hpp"
 
 // ciObject
@@ -201,24 +202,28 @@
 }
 
 // ------------------------------------------------------------------
 // ciObject::should_be_constant()
 bool ciObject::should_be_constant() {
-  if (ScavengeRootsInCode >= 2)  return true;  // force everybody to be a constant
-  if (is_null_object()) return true;
-
+  if (ScavengeRootsInCode >= 2 && !(SCCache::is_on_for_write())) {
+    return true;  // force everybody to be a constant
+  }
+  if (is_null_object()) {
+    return true;
+  }
   ciEnv* env = CURRENT_ENV;
 
-    // We want Strings and Classes to be embeddable by default since
-    // they used to be in the perm world.  Not all Strings used to be
-    // embeddable but there's no easy way to distinguish the interned
-    // from the regulars ones so just treat them all that way.
-    if (klass() == env->String_klass() || klass() == env->Class_klass()) {
-      return true;
-    }
-  if (klass()->is_subclass_of(env->MethodHandle_klass()) ||
-      klass()->is_subclass_of(env->CallSite_klass())) {
+  // We want Strings and Classes to be embeddable by default since
+  // they used to be in the perm world.  Not all Strings used to be
+  // embeddable but there's no easy way to distinguish the interned
+  // from the regulars ones so just treat them all that way.
+  if (klass() == env->String_klass() || klass() == env->Class_klass()) {
+    return true;
+  }
+  if ((klass()->is_subclass_of(env->MethodHandle_klass()) ||
+       klass()->is_subclass_of(env->CallSite_klass())) &&
+      !(SCCache::is_on_for_write())) { // For now disable it when caching startup code.
     // We want to treat these aggressively.
     return true;
   }
 
   return handle() == nullptr;
diff a/src/hotspot/share/ci/ciObjectFactory.cpp b/src/hotspot/share/ci/ciObjectFactory.cpp
--- a/src/hotspot/share/ci/ciObjectFactory.cpp
+++ b/src/hotspot/share/ci/ciObjectFactory.cpp
@@ -42,15 +42,17 @@
 #include "ci/ciTypeArray.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
 #include "ci/ciUtilities.inline.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/vmClasses.hpp"
+#include "compiler/compileTask.hpp"
 #include "compiler/compiler_globals.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/universe.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/trainingData.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/signature.hpp"
 #include "utilities/macros.hpp"
 
 // ciObjectFactory
@@ -248,13 +250,27 @@
   init_ident_of(new_object);
   assert(Universe::heap()->is_in(new_object->get_oop()), "must be");
 
   // Not a perm-space object.
   insert_non_perm(bucket, keyHandle(), new_object);
+  notice_new_object(new_object);
   return new_object;
 }
 
+void ciObjectFactory::notice_new_object(ciBaseObject* new_object) {
+  if (RecordTraining) {
+    ciEnv* env = ciEnv::current();
+    if (env->task() != nullptr) {
+      // Note: task will be null during init_compiler_runtime.
+      CompileTrainingData* tdata = env->task()->training_data();
+      if (tdata != nullptr) {
+        tdata->notice_jit_observation(env, new_object);
+      }
+    }
+  }
+}
+
 int ciObjectFactory::metadata_compare(Metadata* const& key, ciMetadata* const& elt) {
   Metadata* value = elt->constant_encoding();
   if (key < value)      return -1;
   else if (key > value) return 1;
   else                  return 0;
@@ -330,10 +346,11 @@
       // into the table.  We need to recompute our index.
       index = _ci_metadata.find_sorted<Metadata*, ciObjectFactory::metadata_compare>(key, found);
     }
     assert(!found, "no double insert");
     _ci_metadata.insert_before(index, new_object);
+    notice_new_object(new_object);
     return new_object;
   }
   return _ci_metadata.at(index)->as_metadata();
 }
 
diff a/src/hotspot/share/ci/ciObjectFactory.hpp b/src/hotspot/share/ci/ciObjectFactory.hpp
--- a/src/hotspot/share/ci/ciObjectFactory.hpp
+++ b/src/hotspot/share/ci/ciObjectFactory.hpp
@@ -105,10 +105,13 @@
   ciSymbol* get_symbol(Symbol* key);
 
   // Get the ciSymbol corresponding to one of the vmSymbols.
   static ciSymbol* vm_symbol_at(vmSymbolID index);
 
+  // Called on every new object made.
+  void notice_new_object(ciBaseObject* new_object);
+
   // Get the ciMethod representing an unloaded/unfound method.
   ciMethod* get_unloaded_method(ciInstanceKlass* holder,
                                 ciSymbol*        name,
                                 ciSymbol*        signature,
                                 ciInstanceKlass* accessor);
diff a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp
--- a/src/hotspot/share/ci/ciReplay.cpp
+++ b/src/hotspot/share/ci/ciReplay.cpp
@@ -809,11 +809,11 @@
     if (nm != nullptr) {
       nm->make_not_entrant();
     }
     replay_state = this;
     CompileBroker::compile_method(methodHandle(THREAD, method), entry_bci, comp_level,
-                                  methodHandle(), 0, CompileTask::Reason_Replay, THREAD);
+                                  methodHandle(), 0, true, CompileTask::Reason_Replay, THREAD);
     replay_state = nullptr;
   }
 
   // ciMethod <klass> <name> <signature> <invocation_counter> <backedge_counter> <interpreter_invocation_count> <interpreter_throwout_count> <instructions_size>
   void process_ciMethod(TRAPS) {
diff a/src/hotspot/share/ci/ciUtilities.cpp b/src/hotspot/share/ci/ciUtilities.cpp
--- a/src/hotspot/share/ci/ciUtilities.cpp
+++ b/src/hotspot/share/ci/ciUtilities.cpp
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "ci/ciUtilities.hpp"
 #include "gc/shared/cardTableBarrierSet.hpp"
 #include "gc/shared/cardTable.hpp"
 #include "gc/shared/collectedHeap.hpp"
+#include "memory/universe.hpp"
 
 // ciUtilities
 //
 // Miscellaneous internal compiler interface routines.
 
@@ -46,5 +47,15 @@
   BarrierSet* bs = BarrierSet::barrier_set();
   CardTableBarrierSet* ctbs = barrier_set_cast<CardTableBarrierSet>(bs);
   CardTable* ct = ctbs->card_table();
   return ct->byte_map_base();
 }
+
+bool is_card_table_address(address adr) {
+  if (Universe::is_fully_initialized()) {
+    BarrierSet* bs = BarrierSet::barrier_set(); 
+    if (bs->is_a(BarrierSet::CardTableBarrierSet)) {
+      return adr == ci_card_table_address_as<address>();
+    }
+  }
+  return false;
+}
diff a/src/hotspot/share/ci/ciUtilities.hpp b/src/hotspot/share/ci/ciUtilities.hpp
--- a/src/hotspot/share/ci/ciUtilities.hpp
+++ b/src/hotspot/share/ci/ciUtilities.hpp
@@ -54,6 +54,8 @@
 CardTable::CardValue* ci_card_table_address();
 template <typename T> T ci_card_table_address_as() {
   return reinterpret_cast<T>(ci_card_table_address());
 }
 
+bool is_card_table_address(address adr);
+
 #endif // SHARE_CI_CIUTILITIES_HPP
diff a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
--- a/src/hotspot/share/classfile/classLoader.cpp
+++ b/src/hotspot/share/classfile/classLoader.cpp
@@ -67,11 +67,11 @@
 #include "runtime/init.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/os.hpp"
-#include "runtime/perfData.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/threadCritical.hpp"
 #include "runtime/timer.hpp"
 #include "runtime/vm_version.hpp"
 #include "services/management.hpp"
 #include "services/threadService.hpp"
@@ -105,10 +105,11 @@
 
 PerfCounter*    ClassLoader::_perf_accumulated_time = nullptr;
 PerfCounter*    ClassLoader::_perf_classes_inited = nullptr;
 PerfCounter*    ClassLoader::_perf_class_init_time = nullptr;
 PerfCounter*    ClassLoader::_perf_class_init_selftime = nullptr;
+PerfCounter*    ClassLoader::_perf_class_init_bytecodes_count = nullptr;
 PerfCounter*    ClassLoader::_perf_classes_verified = nullptr;
 PerfCounter*    ClassLoader::_perf_class_verify_time = nullptr;
 PerfCounter*    ClassLoader::_perf_class_verify_selftime = nullptr;
 PerfCounter*    ClassLoader::_perf_classes_linked = nullptr;
 PerfCounter*    ClassLoader::_perf_class_link_time = nullptr;
@@ -121,38 +122,60 @@
 PerfCounter*    ClassLoader::_perf_define_appclasses = nullptr;
 PerfCounter*    ClassLoader::_perf_define_appclass_time = nullptr;
 PerfCounter*    ClassLoader::_perf_define_appclass_selftime = nullptr;
 PerfCounter*    ClassLoader::_perf_app_classfile_bytes_read = nullptr;
 PerfCounter*    ClassLoader::_perf_sys_classfile_bytes_read = nullptr;
+PerfCounter*    ClassLoader::_perf_preload_total_time = nullptr;
+PerfCounter*    ClassLoader::_perf_preload_time = nullptr;
+PerfCounter*    ClassLoader::_perf_prelink_time = nullptr;
+PerfCounter*    ClassLoader::_perf_preinit_time = nullptr;
+PerfCounter*    ClassLoader::_perf_preresolve_time = nullptr;
 PerfCounter*    ClassLoader::_perf_ik_link_methods_time = nullptr;
 PerfCounter*    ClassLoader::_perf_method_adapters_time = nullptr;
 PerfCounter*    ClassLoader::_perf_ik_link_methods_count = nullptr;
 PerfCounter*    ClassLoader::_perf_method_adapters_count = nullptr;
 PerfCounter*    ClassLoader::_unsafe_defineClassCallCounter = nullptr;
 PerfCounter*    ClassLoader::_perf_secondary_hash_time = nullptr;
 
-PerfCounter*    ClassLoader::_perf_resolve_indy_time = nullptr;
-PerfCounter*    ClassLoader::_perf_resolve_invokehandle_time = nullptr;
-PerfCounter*    ClassLoader::_perf_resolve_mh_time = nullptr;
-PerfCounter*    ClassLoader::_perf_resolve_mt_time = nullptr;
+PerfTickCounters*    ClassLoader::_perf_resolve_indy_time = nullptr;
+PerfTickCounters*    ClassLoader::_perf_resolve_invokehandle_time = nullptr;
+PerfTickCounters*    ClassLoader::_perf_resolve_mh_time = nullptr;
+PerfTickCounters*    ClassLoader::_perf_resolve_mt_time = nullptr;
 
 PerfCounter*    ClassLoader::_perf_resolve_indy_count = nullptr;
 PerfCounter*    ClassLoader::_perf_resolve_invokehandle_count = nullptr;
 PerfCounter*    ClassLoader::_perf_resolve_mh_count = nullptr;
 PerfCounter*    ClassLoader::_perf_resolve_mt_count = nullptr;
 
 void ClassLoader::print_counters(outputStream *st) {
   st->print_cr("ClassLoader:");
-  st->print_cr("  clinit:               " JLONG_FORMAT "ms / " JLONG_FORMAT " events", ClassLoader::class_init_time_ms(), ClassLoader::class_init_count());
-  st->print_cr("  link methods:         " JLONG_FORMAT "ms / " JLONG_FORMAT " events", Management::ticks_to_ms(_perf_ik_link_methods_time->get_value())   , _perf_ik_link_methods_count->get_value());
-  st->print_cr("  method adapters:      " JLONG_FORMAT "ms / " JLONG_FORMAT " events", Management::ticks_to_ms(_perf_method_adapters_time->get_value())   , _perf_method_adapters_count->get_value());
+  st->print_cr(   "  clinit:               " JLONG_FORMAT_W(6) "us / " JLONG_FORMAT " events",
+               ClassLoader::class_init_time_ms(), ClassLoader::class_init_count());
+  st->print_cr("  link methods:         " JLONG_FORMAT_W(6) "us / " JLONG_FORMAT " events",
+               Management::ticks_to_us(_perf_ik_link_methods_time->get_value())   , _perf_ik_link_methods_count->get_value());
+  st->print_cr("  method adapters:      " JLONG_FORMAT_W(6) "us / " JLONG_FORMAT " events",
+               Management::ticks_to_us(_perf_method_adapters_time->get_value())   , _perf_method_adapters_count->get_value());
+  if (CountBytecodes || CountBytecodesPerThread) {
+    st->print_cr("; executed " JLONG_FORMAT " bytecodes", ClassLoader::class_init_bytecodes_count());
+  }
   st->print_cr("  resolve...");
-  st->print_cr("    invokedynamic:   " JLONG_FORMAT "ms / " JLONG_FORMAT " events", Management::ticks_to_ms(_perf_resolve_indy_time->get_value())         , _perf_resolve_indy_count->get_value());
-  st->print_cr("    invokehandle:    " JLONG_FORMAT "ms / " JLONG_FORMAT " events", Management::ticks_to_ms(_perf_resolve_invokehandle_time->get_value()) , _perf_resolve_invokehandle_count->get_value());
-  st->print_cr("    CP_MethodHandle: " JLONG_FORMAT "ms / " JLONG_FORMAT " events", Management::ticks_to_ms(_perf_resolve_mh_time->get_value())           , _perf_resolve_mh_count->get_value());
-  st->print_cr("    CP_MethodType:   " JLONG_FORMAT "ms / " JLONG_FORMAT " events", Management::ticks_to_ms(_perf_resolve_mt_time->get_value())           , _perf_resolve_mt_count->get_value());
-  st->cr();
+  st->print_cr("    invokedynamic:   " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) / " JLONG_FORMAT_W(5) " events",
+               _perf_resolve_indy_time->elapsed_counter_value_us(),
+               _perf_resolve_indy_time->thread_counter_value_us(),
+               _perf_resolve_indy_count->get_value());
+  st->print_cr("    invokehandle:    " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) / " JLONG_FORMAT_W(5) " events",
+               _perf_resolve_invokehandle_time->elapsed_counter_value_us(),
+               _perf_resolve_invokehandle_time->thread_counter_value_us(),
+               _perf_resolve_invokehandle_count->get_value());
+  st->print_cr("    CP_MethodHandle: " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) / " JLONG_FORMAT_W(5) " events",
+               _perf_resolve_mh_time->elapsed_counter_value_us(),
+               _perf_resolve_mh_time->thread_counter_value_us(),
+               _perf_resolve_mh_count->get_value());
+  st->print_cr("    CP_MethodType:   " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) / " JLONG_FORMAT_W(5) " events",
+               _perf_resolve_mt_time->elapsed_counter_value_us(),
+               _perf_resolve_mt_time->thread_counter_value_us(),
+               _perf_resolve_mt_count->get_value());
 }
 
 GrowableArray<ModuleClassPathList*>* ClassLoader::_patch_mod_entries = nullptr;
 GrowableArray<ModuleClassPathList*>* ClassLoader::_exploded_entries = nullptr;
 ClassPathEntry* ClassLoader::_jrt_entry = nullptr;
@@ -301,21 +324,35 @@
   FREE_RESOURCE_ARRAY_IN_THREAD(current, char, path, path_len);
   return nullptr;
 }
 
 ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name,
-                                     bool is_boot_append, bool from_class_path_attr) : ClassPathEntry() {
+                                     bool from_class_path_attr) : ClassPathEntry() {
   _zip = zip;
   _zip_name = copy_path(zip_name);
   _from_class_path_attr = from_class_path_attr;
 }
 
 ClassPathZipEntry::~ClassPathZipEntry() {
   ZipLibrary::close(_zip);
   FREE_C_HEAP_ARRAY(char, _zip_name);
 }
 
+bool ClassPathZipEntry::has_entry(JavaThread* current, const char* name) {
+  ThreadToNativeFromVM ttn(current);
+  // check whether zip archive contains name
+  jint name_len;
+  jint filesize;
+  jzentry* entry = ZipLibrary::find_entry(_zip, name, &filesize, &name_len);
+  if (entry == nullptr) {
+    return false;
+  } else {
+     ZipLibrary::free_entry(_zip, entry);
+    return true;
+  }
+}
+
 u1* ClassPathZipEntry::open_entry(JavaThread* current, const char* name, jint* filesize, bool nul_terminate) {
   // enable call to C land
   ThreadToNativeFromVM ttn(current);
   // check whether zip archive contains name
   jint name_len;
@@ -336,10 +373,12 @@
     if (sizeof(size) == sizeof(uint32_t) && size == UINT_MAX) {
       return nullptr; // 32-bit integer overflow will occur.
     }
     size++;
   }
+
+  // ZIP_ReadEntry also frees zentry
   buffer = NEW_RESOURCE_ARRAY(u1, size);
   if (!ZipLibrary::read_entry(_zip, entry, buffer, filename)) {
     return nullptr;
   }
 
@@ -538,15 +577,44 @@
   assert(CDSConfig::is_dumping_archive(), "sanity");
 
   ResourceMark rm(current);
   ClasspathStream cp_stream(class_path);
 
+  int exclusion_start_index = INT_MAX;
+  size_t limit = SIZE_MAX;
+  if (CacheOnlyClassesIn != nullptr) {
+    char* appclasspath = Arguments::get_appclasspath();
+    bool bad = false;
+    if (strstr(appclasspath, CacheOnlyClassesIn) != appclasspath) {
+      bad = true;
+    } else {
+      limit = strlen(CacheOnlyClassesIn);
+      if (limit > 0 && limit < strlen(appclasspath)
+          && CacheOnlyClassesIn[limit-1] != os::path_separator()[0]
+          && appclasspath[limit] != os::path_separator()[0]) {
+        bad = true;
+      }
+    }
+    if (bad) {
+      vm_exit_during_initialization(err_msg("CacheOnlyClassesIn \"%s\" must be a proper prefix of the CLASSPATH \"%s\"",
+                                            CacheOnlyClassesIn, appclasspath));
+    }
+  }
+
   while (cp_stream.has_next()) {
+    if (cp_stream.num_chars_consumed() >= limit && exclusion_start_index == INT_MAX) {
+      exclusion_start_index = num_boot_classpath_entries() + num_app_classpath_entries();
+    }
     const char* path = cp_stream.get_next();
     update_class_path_entry_list(current, path, /* check_for_duplicates */ true,
                                  /* is_boot_append */ false, /* from_class_path_attr */ false);
   }
+
+  ClassLoaderExt::set_app_class_exclusion_start_path_index(exclusion_start_index);
+  if (exclusion_start_index != INT_MAX) {
+    log_info(cds)("Exclude all app classes whose shared_classpath_index is greater than %d", exclusion_start_index);
+  }
 }
 
 void ClassLoader::add_to_module_path_entries(const char* path,
                                              ClassPathEntry* entry) {
   assert(entry != nullptr, "ClassPathEntry should not be nullptr");
@@ -761,11 +829,11 @@
       return nullptr;
     }
     char* error_msg = nullptr;
     jzfile* zip = open_zip_file(canonical_path, &error_msg, current);
     if (zip != nullptr && error_msg == nullptr) {
-      new_entry = new ClassPathZipEntry(zip, path, is_boot_append, from_class_path_attr);
+      new_entry = new ClassPathZipEntry(zip, path, from_class_path_attr);
     } else {
 #if INCLUDE_CDS
       ClassLoaderExt::set_has_non_jar_in_classpath();
 #endif
       return nullptr;
@@ -781,11 +849,11 @@
 }
 
 
 // Create a class path zip entry for a given path (return null if not found
 // or zip/JAR file cannot be opened)
-ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path, bool is_boot_append) {
+ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path) {
   // check for a regular file
   struct stat st;
   if (os::stat(path, &st) == 0) {
     if ((st.st_mode & S_IFMT) == S_IFREG) {
       JavaThread* thread = JavaThread::current();
@@ -794,11 +862,11 @@
       if (canonical_path != nullptr) {
         char* error_msg = nullptr;
         jzfile* zip = open_zip_file(canonical_path, &error_msg, thread);
         if (zip != nullptr && error_msg == nullptr) {
           // create using canonical path
-          return new ClassPathZipEntry(zip, canonical_path, is_boot_append, false);
+          return new ClassPathZipEntry(zip, canonical_path, false);
         }
       }
     }
   }
   return nullptr;
@@ -1432,10 +1500,11 @@
     NEWPERFTICKCOUNTER(_perf_class_link_time, SUN_CLS, "classLinkedTime");
     NEWPERFTICKCOUNTER(_perf_class_link_selftime, SUN_CLS, "classLinkedTime.self");
     NEWPERFEVENTCOUNTER(_perf_classes_inited, SUN_CLS, "initializedClasses");
     NEWPERFEVENTCOUNTER(_perf_classes_linked, SUN_CLS, "linkedClasses");
     NEWPERFEVENTCOUNTER(_perf_classes_verified, SUN_CLS, "verifiedClasses");
+    NEWPERFEVENTCOUNTER(_perf_class_init_bytecodes_count, SUN_CLS, "clinitBytecodesCount");
 
     NEWPERFTICKCOUNTER(_perf_shared_classload_time, SUN_CLS, "sharedClassLoadTime");
     NEWPERFTICKCOUNTER(_perf_sys_classload_time, SUN_CLS, "sysClassLoadTime");
     NEWPERFTICKCOUNTER(_perf_app_classload_time, SUN_CLS, "appClassLoadTime");
     NEWPERFTICKCOUNTER(_perf_app_classload_selftime, SUN_CLS, "appClassLoadTime.self");
@@ -1446,20 +1515,26 @@
     NEWPERFBYTECOUNTER(_perf_app_classfile_bytes_read, SUN_CLS, "appClassBytes");
     NEWPERFBYTECOUNTER(_perf_sys_classfile_bytes_read, SUN_CLS, "sysClassBytes");
     NEWPERFEVENTCOUNTER(_unsafe_defineClassCallCounter, SUN_CLS, "unsafeDefineClassCalls");
     NEWPERFTICKCOUNTER(_perf_secondary_hash_time, SUN_CLS, "secondarySuperHashTime");
 
-    if (log_is_enabled(Info, perf, class, link)) {
+    if (log_is_enabled(Info, perf, class, link) || log_is_enabled(Info, init)) {
+      NEWPERFTICKCOUNTER(_perf_preload_total_time, SUN_CLS, "preloadTotalTime");
+      NEWPERFTICKCOUNTER(_perf_preload_time, SUN_CLS, "preloadTime");
+      NEWPERFTICKCOUNTER(_perf_prelink_time, SUN_CLS, "prelinkTime");
+      NEWPERFTICKCOUNTER(_perf_preinit_time, SUN_CLS, "preinitTime");
+      NEWPERFTICKCOUNTER(_perf_preresolve_time, SUN_CLS, "preresolveTime");
+
       NEWPERFTICKCOUNTER(_perf_ik_link_methods_time, SUN_CLS, "linkMethodsTime");
       NEWPERFTICKCOUNTER(_perf_method_adapters_time, SUN_CLS, "makeAdaptersTime");
       NEWPERFEVENTCOUNTER(_perf_ik_link_methods_count, SUN_CLS, "linkMethodsCount");
       NEWPERFEVENTCOUNTER(_perf_method_adapters_count, SUN_CLS, "makeAdaptersCount");
 
-      NEWPERFTICKCOUNTER(_perf_resolve_indy_time, SUN_CLS, "resolve_invokedynamic_time");
-      NEWPERFTICKCOUNTER(_perf_resolve_invokehandle_time, SUN_CLS, "resolve_invokehandle_time");
-      NEWPERFTICKCOUNTER(_perf_resolve_mh_time, SUN_CLS, "resolve_MethodHandle_time");
-      NEWPERFTICKCOUNTER(_perf_resolve_mt_time, SUN_CLS, "resolve_MethodType_time");
+      NEWPERFTICKCOUNTERS(_perf_resolve_indy_time, SUN_CLS, "resolve_invokedynamic_time");
+      NEWPERFTICKCOUNTERS(_perf_resolve_invokehandle_time, SUN_CLS, "resolve_invokehandle_time");
+      NEWPERFTICKCOUNTERS(_perf_resolve_mh_time, SUN_CLS, "resolve_MethodHandle_time");
+      NEWPERFTICKCOUNTERS(_perf_resolve_mt_time, SUN_CLS, "resolve_MethodType_time");
 
       NEWPERFEVENTCOUNTER(_perf_resolve_indy_count, SUN_CLS, "resolve_invokedynamic_count");
       NEWPERFEVENTCOUNTER(_perf_resolve_invokehandle_count, SUN_CLS, "resolve_invokehandle_count");
       NEWPERFEVENTCOUNTER(_perf_resolve_mh_count, SUN_CLS, "resolve_MethodHandle_count");
       NEWPERFEVENTCOUNTER(_perf_resolve_mt_count, SUN_CLS, "resolve_MethodType_count");
@@ -1548,10 +1623,14 @@
 jlong ClassLoader::classloader_time_ms() {
   return UsePerfData ?
     Management::ticks_to_ms(_perf_accumulated_time->get_value()) : -1;
 }
 
+jlong ClassLoader::class_init_bytecodes_count() {
+  return UsePerfData ? _perf_class_init_bytecodes_count->get_value() : -1;
+}
+
 jlong ClassLoader::class_init_count() {
   return UsePerfData ? _perf_classes_inited->get_value() : -1;
 }
 
 jlong ClassLoader::class_init_time_ms() {
diff a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp
--- a/src/hotspot/share/classfile/classLoader.hpp
+++ b/src/hotspot/share/classfile/classLoader.hpp
@@ -93,12 +93,13 @@
   bool _from_class_path_attr; // From the "Class-path" attribute of a jar file
  public:
   bool is_jar_file() const { return true;  }
   bool from_class_path_attr() const { return _from_class_path_attr; }
   const char* name() const { return _zip_name; }
-  ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append, bool from_class_path_attr);
+  ClassPathZipEntry(jzfile* zip, const char* zip_name, bool from_class_path_attr);
   virtual ~ClassPathZipEntry();
+  bool has_entry(JavaThread* current, const char* name);
   u1* open_entry(JavaThread* current, const char* name, jint* filesize, bool nul_terminate);
   ClassFileStream* open_stream(JavaThread* current, const char* name);
 };
 
 
@@ -150,10 +151,11 @@
   // Performance counters
   static PerfCounter* _perf_accumulated_time;
   static PerfCounter* _perf_classes_inited;
   static PerfCounter* _perf_class_init_time;
   static PerfCounter* _perf_class_init_selftime;
+  static PerfCounter* _perf_class_init_bytecodes_count;
   static PerfCounter* _perf_classes_verified;
   static PerfCounter* _perf_class_verify_time;
   static PerfCounter* _perf_class_verify_selftime;
   static PerfCounter* _perf_classes_linked;
   static PerfCounter* _perf_class_link_time;
@@ -166,19 +168,24 @@
   static PerfCounter* _perf_define_appclasses;
   static PerfCounter* _perf_define_appclass_time;
   static PerfCounter* _perf_define_appclass_selftime;
   static PerfCounter* _perf_app_classfile_bytes_read;
   static PerfCounter* _perf_sys_classfile_bytes_read;
+  static PerfCounter* _perf_preload_total_time;
+  static PerfCounter* _perf_preload_time;
+  static PerfCounter* _perf_prelink_time;
+  static PerfCounter* _perf_preinit_time;
+  static PerfCounter* _perf_preresolve_time;
   static PerfCounter* _perf_ik_link_methods_time;
   static PerfCounter* _perf_method_adapters_time;
   static PerfCounter* _perf_ik_link_methods_count;
   static PerfCounter* _perf_method_adapters_count;
 
-  static PerfCounter* _perf_resolve_indy_time;
-  static PerfCounter* _perf_resolve_invokehandle_time;
-  static PerfCounter* _perf_resolve_mh_time;
-  static PerfCounter* _perf_resolve_mt_time;
+  static PerfTickCounters* _perf_resolve_indy_time;
+  static PerfTickCounters* _perf_resolve_invokehandle_time;
+  static PerfTickCounters* _perf_resolve_mh_time;
+  static PerfTickCounters* _perf_resolve_mt_time;
 
   static PerfCounter* _perf_resolve_indy_count;
   static PerfCounter* _perf_resolve_invokehandle_count;
   static PerfCounter* _perf_resolve_mh_count;
   static PerfCounter* _perf_resolve_mt_count;
@@ -299,25 +306,32 @@
   static PerfCounter* perf_define_appclass_time()     { return _perf_define_appclass_time; }
   static PerfCounter* perf_define_appclass_selftime() { return _perf_define_appclass_selftime; }
   static PerfCounter* perf_app_classfile_bytes_read() { return _perf_app_classfile_bytes_read; }
   static PerfCounter* perf_sys_classfile_bytes_read() { return _perf_sys_classfile_bytes_read; }
 
+  static PerfCounter* perf_preload_total_time() { return _perf_preload_total_time; }
+  static PerfCounter* perf_preload_time() { return _perf_preload_time; }
+  static PerfCounter* perf_prelink_time() { return _perf_prelink_time; }
+  static PerfCounter* perf_preinit_time() { return _perf_preinit_time; }
+  static PerfCounter* perf_preresolve_time() { return _perf_preresolve_time; }
   static PerfCounter* perf_ik_link_methods_time() { return _perf_ik_link_methods_time; }
   static PerfCounter* perf_method_adapters_time() { return _perf_method_adapters_time; }
   static PerfCounter* perf_ik_link_methods_count() { return _perf_ik_link_methods_count; }
   static PerfCounter* perf_method_adapters_count() { return _perf_method_adapters_count; }
 
-  static PerfCounter* perf_resolve_invokedynamic_time() { return _perf_resolve_indy_time; }
-  static PerfCounter* perf_resolve_invokehandle_time() { return _perf_resolve_invokehandle_time; }
-  static PerfCounter* perf_resolve_method_handle_time() { return _perf_resolve_mh_time; }
-  static PerfCounter* perf_resolve_method_type_time() { return _perf_resolve_mt_time; }
+  static PerfTickCounters* perf_resolve_invokedynamic_time() { return _perf_resolve_indy_time; }
+  static PerfTickCounters* perf_resolve_invokehandle_time() { return _perf_resolve_invokehandle_time; }
+  static PerfTickCounters* perf_resolve_method_handle_time() { return _perf_resolve_mh_time; }
+  static PerfTickCounters* perf_resolve_method_type_time() { return _perf_resolve_mt_time; }
 
   static PerfCounter* perf_resolve_invokedynamic_count() { return _perf_resolve_indy_count; }
   static PerfCounter* perf_resolve_invokehandle_count() { return _perf_resolve_invokehandle_count; }
   static PerfCounter* perf_resolve_method_handle_count() { return _perf_resolve_mh_count; }
   static PerfCounter* perf_resolve_method_type_count() { return _perf_resolve_mt_count; }
 
+  static PerfCounter* perf_class_init_bytecodes_count() { return _perf_class_init_bytecodes_count; }
+
   static void print_counters(outputStream *st);
 
   // Record how many calls to Unsafe_DefineClass
   static PerfCounter* unsafe_defineClassCallCounter() {
     return _unsafe_defineClassCallCounter;
@@ -406,16 +420,17 @@
   static jlong class_init_count();
   static jlong class_init_time_ms();
   static jlong class_verify_time_ms();
   static jlong class_link_count();
   static jlong class_link_time_ms();
+  static jlong class_init_bytecodes_count();
 
   // adds a class path to the boot append entries
   static void add_to_boot_append_entries(ClassPathEntry* new_entry);
 
   // creates a class path zip entry (returns null if JAR file cannot be opened)
-  static ClassPathZipEntry* create_class_path_zip_entry(const char *apath, bool is_boot_append);
+  static ClassPathZipEntry* create_class_path_zip_entry(const char *apath);
 
   static bool string_ends_with(const char* str, const char* str_to_find);
 
   // Extract package name from a fully qualified class name
   // *bad_class_name is set to true if there's a problem with parsing class_name, to
diff a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp
--- a/src/hotspot/share/classfile/classLoaderDataShared.cpp
+++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp
@@ -100,11 +100,11 @@
     loader_data->modules() ->init_archived_entries(_modules);
   }
 }
 
 void ArchivedClassLoaderData::restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops) {
-  assert(CDSConfig::is_using_archive(), "must be");
+  assert(CDSConfig::is_using_full_module_graph(), "must be");
   assert_valid(loader_data);
   if (_modules != nullptr) { // Could be null if we have archived no modules for platform/system loaders
     ModuleEntryTable* modules = loader_data->modules();
     PackageEntryTable* packages = loader_data->packages();
 
@@ -118,11 +118,11 @@
     }
   }
 }
 
 void ArchivedClassLoaderData::clear_archived_oops() {
-  assert(CDSConfig::is_using_archive(), "must be");
+  assert(!CDSConfig::is_using_full_module_graph(), "must be");
   if (_modules != nullptr) {
     for (int i = 0; i < _modules->length(); i++) {
       _modules->at(i)->clear_archived_oops();
     }
   }
diff a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp
--- a/src/hotspot/share/classfile/classLoaderExt.cpp
+++ b/src/hotspot/share/classfile/classLoaderExt.cpp
@@ -57,10 +57,11 @@
 jshort ClassLoaderExt::_max_used_path_index = 0;
 int ClassLoaderExt::_num_module_paths = 0;
 bool ClassLoaderExt::_has_app_classes = false;
 bool ClassLoaderExt::_has_platform_classes = false;
 bool ClassLoaderExt::_has_non_jar_in_classpath = false;
+int ClassLoaderExt::_app_class_exclusion_start_path_index = INT_MAX;
 
 void ClassLoaderExt::append_boot_classpath(ClassPathEntry* new_entry) {
   if (CDSConfig::is_using_archive()) {
     warning("Sharing is only supported for boot loader classes because bootstrap classpath has been appended");
     FileMapInfo::current_info()->set_has_platform_or_app_classes(false);
@@ -103,10 +104,22 @@
     GrowableArray<const char*>* _module_paths;
    public:
     ModulePathsGatherer(JavaThread* current, GrowableArray<const char*>* module_paths) :
       _current(current), _module_paths(module_paths) {}
     void do_module(ModuleEntry* m) {
+      if (m->location() == nullptr) {
+        // This is a dynamic generated module (which we loaded from the static archive) for supporting
+        // dynamic proxies. We don't archive any other such modules.
+        // No need to include such modules in _module_path.
+        assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
+        assert(Modules::is_dynamic_proxy_module(m), "must be");
+        if (log_is_enabled(Info, cds, dynamic, proxy)) {
+          ResourceMark rm;
+          log_info(cds, dynamic, proxy)("Archived dynamic module: %s", m->name()->as_C_string());
+        }
+        return;
+      }
       char* uri = m->location()->as_C_string();
       if (strncmp(uri, "file:", 5) == 0) {
         char* path = ClassLoader::uri_to_path(uri);
         extract_jar_files_from_path(path, _module_paths);
       }
@@ -338,6 +351,78 @@
     log_warning(cds)("CDS heap objects cannot be written because class %s maybe modified by ClassFileLoadHook.",
                      result->external_name());
     HeapShared::disable_writing();
   }
 #endif // INCLUDE_CDS_JAVA_HEAP
+  if (CDSConfig::is_dumping_preimage_static_archive() || CDSConfig::is_dumping_dynamic_archive()) {
+    check_invalid_classpath_index(classpath_index, result);
+  }
 }
+
+ClassPathEntry* ClassLoaderExt::get_class_path_entry(s2 classpath_index) {
+  if (classpath_index < 0) {
+    return nullptr;
+  }
+
+  if (classpath_index == 0) {
+    assert(has_jrt_entry(), "CDS requires modules image");
+    return get_jrt_entry();
+  }
+
+  // Iterate over -Xbootclasspath, if any;
+  int i = 0;
+  for (ClassPathEntry* cpe = first_append_entry(); cpe != nullptr; cpe = cpe->next()) {
+    i++;
+    if (i == classpath_index) {
+      return cpe;
+    }
+  }
+
+  // Iterate over -cp, if any
+  for (ClassPathEntry* cpe = app_classpath_entries(); cpe != nullptr; cpe = cpe->next()) {
+    i++;
+    if (i == classpath_index) {
+      return cpe;
+    }
+  }
+
+  // Iterate over --module-path, if any
+  for (ClassPathEntry* cpe = module_path_entries(); cpe != nullptr; cpe = cpe->next()) {
+    i++;
+    if (i == classpath_index) {
+      return cpe;
+    }
+  }
+
+  return nullptr;
+}
+
+// It's possible to use reflection+setAccessible to call into ClassLoader::defineClass() to
+// pretend that a dynamically generated class comes from a JAR file in the classpath.
+// Detect such classes and exclude them from the archive.
+void ClassLoaderExt::check_invalid_classpath_index(s2 classpath_index, InstanceKlass* ik) {
+  ClassPathEntry *cpe = get_class_path_entry(classpath_index);
+  if (cpe != nullptr && cpe->is_jar_file()) {
+    ClassPathZipEntry *zip = (ClassPathZipEntry*)cpe;
+    JavaThread* current = JavaThread::current();
+    ResourceMark rm(current);
+    const char* const class_name = ik->name()->as_C_string();
+    const char* const file_name = file_name_for_class_name(class_name,
+                                                           ik->name()->utf8_length());
+    if (!zip->has_entry(current, file_name)) {
+      log_warning(cds)("class %s cannot be archived because it was not define from %s as claimed",
+                       class_name, zip->name());
+      ik->set_shared_classpath_index(-1);
+    }
+  }
+}
+
+// -XX:CacheOnlyClassesIn=
+bool ClassLoaderExt::should_be_excluded(InstanceKlass* k) {
+  int path_index = k->shared_classpath_index();
+  if (path_index >= _app_class_exclusion_start_path_index  && path_index < _app_module_paths_start_index) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
diff a/src/hotspot/share/classfile/classLoaderExt.hpp b/src/hotspot/share/classfile/classLoaderExt.hpp
--- a/src/hotspot/share/classfile/classLoaderExt.hpp
+++ b/src/hotspot/share/classfile/classLoaderExt.hpp
@@ -56,10 +56,17 @@
   static bool _has_non_jar_in_classpath;
 
   static char* read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size, bool clean_text);
   static bool has_jar_suffix(const char* filename);
 
+  // All classes whose path_index is within
+  //     [_app_class_exclusion_start_path_index ... _app_module_paths_start_index)
+  // are excluded from the archive. See -XX:CacheOnlyClassesIn.
+  static int  _app_class_exclusion_start_path_index;
+
+  static ClassPathEntry* get_class_path_entry(s2 classpath_index);
+  static void check_invalid_classpath_index(s2 classpath_index, InstanceKlass* ik);
 public:
   static void process_jar_manifest(JavaThread* current, ClassPathEntry* entry);
 
   // Called by JVMTI code to add boot classpath
   static void append_boot_classpath(ClassPathEntry* new_entry);
@@ -125,9 +132,16 @@
     _has_platform_classes = true;
   }
   static void set_has_non_jar_in_classpath() {
     _has_non_jar_in_classpath = true;
   }
+
+  // -XX:CacheOnlyClassesIn=
+  static bool should_be_excluded(InstanceKlass* k);
+  static void set_app_class_exclusion_start_path_index(int i) {
+    _app_class_exclusion_start_path_index = i;
+  }
+
 #endif // INCLUDE_CDS
 };
 
 #endif // SHARE_CLASSFILE_CLASSLOADEREXT_HPP
diff a/src/hotspot/share/classfile/classPrinter.cpp b/src/hotspot/share/classfile/classPrinter.cpp
--- a/src/hotspot/share/classfile/classPrinter.cpp
+++ b/src/hotspot/share/classfile/classPrinter.cpp
@@ -27,11 +27,11 @@
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/classPrinter.hpp"
 #include "memory/iterator.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/instanceKlass.hpp"
-#include "oops/klass.hpp"
+#include "oops/klass.inline.hpp"
 #include "oops/method.hpp"
 #include "oops/symbol.hpp"
 #include "utilities/ostream.hpp"
 
 class ClassPrinter::KlassPrintClosure : public LockedClassesDo {
@@ -53,10 +53,11 @@
       _method_name_pattern(method_name_pattern),
       _method_signature_pattern(method_signature_pattern),
       _always_print_class_name(always_print_class_name),
       _flags(flags), _st(st), _num(0), _has_printed_methods(false)
   {
+    _flags |= (PRINT_PROFILE);
     if (has_mode(_flags, PRINT_METHOD_HANDLE)) {
       _flags |= (PRINT_METHOD_NAME | PRINT_BYTECODE);
     }
     if (has_mode(_flags, PRINT_DYNAMIC)) {
       _flags |= (PRINT_METHOD_NAME | PRINT_BYTECODE);
@@ -79,11 +80,12 @@
   static bool match(const char* pattern, Symbol* sym) {
     return (pattern == nullptr || sym->is_star_match(pattern));
   }
 
   void print_klass_name(InstanceKlass* ik) {
-    _st->print("[%3d] " INTPTR_FORMAT " class %s ", _num++, p2i(ik), ik->name()->as_C_string());
+    _st->print("[%3d] " INTPTR_FORMAT " class %s mirror: " INTPTR_FORMAT " ", _num++,
+               p2i(ik), ik->name()->as_C_string(), p2i(ik->java_mirror()));
     ik->class_loader_data()->print_value_on(_st);
     _st->cr();
   }
 
   void print_instance_klass(InstanceKlass* ik) {
diff a/src/hotspot/share/classfile/classPrinter.hpp b/src/hotspot/share/classfile/classPrinter.hpp
--- a/src/hotspot/share/classfile/classPrinter.hpp
+++ b/src/hotspot/share/classfile/classPrinter.hpp
@@ -50,10 +50,11 @@
     PRINT_METHOD_NAME       = 1 << 0,
     PRINT_BYTECODE          = 1 << 1,
     PRINT_BYTECODE_ADDR     = 1 << 2,
     PRINT_DYNAMIC           = 1 << 3, // extra information for invokedynamic (and dynamic constant ...)
     PRINT_METHOD_HANDLE     = 1 << 4, // extra information for invokehandle
+    PRINT_PROFILE           = 1 << 5, // print MDO contents
   };
   static bool has_mode(int flags, Mode mode) {
     return (flags & static_cast<int>(mode)) != 0;
   }
 
diff a/src/hotspot/share/classfile/compactHashtable.hpp b/src/hotspot/share/classfile/compactHashtable.hpp
--- a/src/hotspot/share/classfile/compactHashtable.hpp
+++ b/src/hotspot/share/classfile/compactHashtable.hpp
@@ -302,10 +302,30 @@
         }
       }
     }
   }
 
+  template<typename Function>
+  inline void iterate(Function function) const { // lambda enabled API
+    for (u4 i = 0; i < _bucket_count; i++) {
+      u4 bucket_info = _buckets[i];
+      u4 bucket_offset = BUCKET_OFFSET(bucket_info);
+      int bucket_type = BUCKET_TYPE(bucket_info);
+      u4* entry = _entries + bucket_offset;
+
+      if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
+        function(decode(entry[0]));
+      } else {
+        u4* entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
+        while (entry < entry_max) {
+          function(decode(entry[1]));
+          entry += 2;
+        }
+      }
+    }
+  }
+
   void print_table_statistics(outputStream* st, const char* name) {
     st->print_cr("%s statistics:", name);
     int total_entries = 0;
     int max_bucket = 0;
     for (u4 i = 0; i < _bucket_count; i++) {
diff a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp
--- a/src/hotspot/share/classfile/javaClasses.cpp
+++ b/src/hotspot/share/classfile/javaClasses.cpp
@@ -1143,11 +1143,15 @@
       // Set after k->java_mirror() is published, because compiled code running
       // concurrently doesn't expect a k to have a null java_mirror.
       release_set_array_klass(comp_mirror(), k);
     }
     if (CDSConfig::is_dumping_heap()) {
-      create_scratch_mirror(k, CHECK);
+      if (CDSConfig::is_dumping_protection_domains()) {
+        create_scratch_mirror(k, protection_domain, CHECK);
+      } else {
+        create_scratch_mirror(k, Handle() /* null protection_domain*/, CHECK);
+      }
     }
   } else {
     assert(fixup_mirror_list() != nullptr, "fixup_mirror_list not initialized");
     fixup_mirror_list()->push(k);
   }
@@ -1163,19 +1167,19 @@
 // produces the same result as /*runtime*/create_mirror().
 //
 // Note: we archive the "scratch mirror" instead of k->java_mirror(), because the
 // latter may contain dumptime-specific information that cannot be archived
 // (e.g., ClassLoaderData*, or static fields that are modified by Java code execution).
-void java_lang_Class::create_scratch_mirror(Klass* k, TRAPS) {
+void java_lang_Class::create_scratch_mirror(Klass* k, Handle protection_domain, TRAPS) {
   if (k->class_loader() != nullptr &&
       k->class_loader() != SystemDictionary::java_platform_loader() &&
       k->class_loader() != SystemDictionary::java_system_loader()) {
     // We only archive the mirrors of classes loaded by the built-in loaders
     return;
   }
 
-  Handle protection_domain, classData; // set to null. Will be reinitialized at runtime
+  Handle classData; // set to null. Will be reinitialized at runtime
   Handle mirror;
   Handle comp_mirror;
   allocate_mirror(k, /*is_scratch=*/true, protection_domain, classData, mirror, comp_mirror, CHECK);
 
   if (comp_mirror() != nullptr) {
@@ -1302,10 +1306,19 @@
 void java_lang_Class::set_class_data(oop java_class, oop class_data) {
   assert(_classData_offset != 0, "must be set");
   java_class->obj_field_put(_classData_offset, class_data);
 }
 
+oop java_lang_Class::reflection_data(oop java_class) {
+  assert(_reflectionData_offset != 0, "must be set");
+  return java_class->obj_field(_reflectionData_offset);
+}
+
+bool java_lang_Class::has_reflection_data(oop java_class) {
+  return (java_lang_Class::reflection_data(java_class) != nullptr);
+}
+
 void java_lang_Class::set_reflection_data(oop java_class, oop reflection_data) {
   assert(_reflectionData_offset != 0, "must be set");
   java_class->obj_field_put(_reflectionData_offset, reflection_data);
 }
 
@@ -1497,11 +1510,11 @@
   macro(_class_loader_offset,        k, "classLoader",         classloader_signature,  false); \
   macro(_component_mirror_offset,    k, "componentType",       class_signature,        false); \
   macro(_module_offset,              k, "module",              module_signature,       false); \
   macro(_name_offset,                k, "name",                string_signature,       false); \
   macro(_classData_offset,           k, "classData",           object_signature,       false); \
-  macro(_reflectionData_offset,      k, "reflectionData",      java_lang_ref_SoftReference_signature, false); \
+  macro(_reflectionData_offset,      k, "reflectionData",      class_ReflectionData_signature, false); \
   macro(_signers_offset,             k, "signers",             object_array_signature, false);
 
 void java_lang_Class::compute_offsets() {
   if (_offsets_computed) {
     return;
@@ -2274,10 +2287,14 @@
 
 void java_lang_Throwable::clear_stacktrace(oop throwable) {
   set_stacktrace(throwable, nullptr);
 }
 
+oop java_lang_Throwable::create_exception_instance(Symbol* class_name, TRAPS) {
+  Klass* k = SystemDictionary::resolve_or_fail(class_name, true, CHECK_NULL);
+  return InstanceKlass::cast(k)->allocate_instance(CHECK_NULL);
+}
 
 void java_lang_Throwable::print(oop throwable, outputStream* st) {
   ResourceMark rm;
   Klass* k = throwable->klass();
   assert(k != nullptr, "just checking");
diff a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp
--- a/src/hotspot/share/classfile/javaClasses.hpp
+++ b/src/hotspot/share/classfile/javaClasses.hpp
@@ -280,11 +280,11 @@
   static void fixup_mirror(Klass* k, TRAPS);
   static oop  create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS);
 
   // Archiving
   static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
-  static void create_scratch_mirror(Klass* k, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
+  static void create_scratch_mirror(Klass* k, Handle protection_domain, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
   static bool restore_archived_mirror(Klass *k, Handle class_loader, Handle module,
                                       Handle protection_domain,
                                       TRAPS) NOT_CDS_JAVA_HEAP_RETURN_(false);
 
   static void fixup_module_field(Klass* k, Handle module);
@@ -320,11 +320,14 @@
   }
   static oop  component_mirror(oop java_class);
   static objArrayOop signers(oop java_class);
   static oop  class_data(oop java_class);
   static void set_class_data(oop java_class, oop classData);
+
+  static oop  reflection_data(oop java_class);
   static void set_reflection_data(oop java_class, oop reflection_data);
+  static bool has_reflection_data(oop java_class);
 
   static int component_mirror_offset() { return _component_mirror_offset; }
 
   static oop class_loader(oop java_class);
   static void set_module(oop java_class, oop module);
@@ -667,11 +670,13 @@
   static void get_stack_trace_elements(int depth, Handle backtrace, objArrayHandle stack_trace, TRAPS);
 
   // For recreating class initialization error exceptions.
   static Handle create_initialization_error(JavaThread* current, Handle throwable);
 
-  // Printing
+  static oop create_exception_instance(Symbol* class_name, TRAPS);
+
+    // Printing
   static void print(oop throwable, outputStream* st);
   static void print_stack_trace(Handle throwable, outputStream* st);
   static void java_printStackTrace(Handle throwable, TRAPS);
   // Debugging
   friend class JavaClasses;
diff a/src/hotspot/share/classfile/klassFactory.cpp b/src/hotspot/share/classfile/klassFactory.cpp
--- a/src/hotspot/share/classfile/klassFactory.cpp
+++ b/src/hotspot/share/classfile/klassFactory.cpp
@@ -21,10 +21,11 @@
 * questions.
 *
 */
 
 #include "precompiled.hpp"
+#include "cds/cds_globals.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/filemap.hpp"
 #include "classfile/classFileParser.hpp"
 #include "classfile/classFileStream.hpp"
 #include "classfile/classLoader.hpp"
diff a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp
--- a/src/hotspot/share/classfile/moduleEntry.cpp
+++ b/src/hotspot/share/classfile/moduleEntry.cpp
@@ -30,10 +30,11 @@
 #include "cds/heapShared.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderData.inline.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/moduleEntry.hpp"
+#include "classfile/modules.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "jni.h"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
 #include "memory/resourceArea.hpp"
@@ -430,13 +431,20 @@
 }
 
 // This function is used to archive ModuleEntry::_reads and PackageEntry::_qualified_exports.
 // GrowableArray cannot be directly archived, as it needs to be expandable at runtime.
 // Write it out as an Array, and convert it back to GrowableArray at runtime.
-Array<ModuleEntry*>* ModuleEntry::write_growable_array(GrowableArray<ModuleEntry*>* array) {
+Array<ModuleEntry*>* ModuleEntry::write_growable_array(ModuleEntry* module, GrowableArray<ModuleEntry*>* array) {
   Array<ModuleEntry*>* archived_array = nullptr;
   int length = (array == nullptr) ? 0 : array->length();
+  if (module->is_named()) {
+    if (Modules::is_dynamic_proxy_module(module)) {
+      // This is a dynamically generated module. Its opens and exports will be
+      // restored at runtime in the Java code. See comments in ArchivedData::restore().
+      return nullptr;
+    }
+  }
   if (length > 0) {
     archived_array = ArchiveBuilder::new_ro_array<ModuleEntry*>(length);
     for (int i = 0; i < length; i++) {
       ModuleEntry* archived_entry = get_archived_entry(array->at(i));
       archived_array->at_put(i, archived_entry);
@@ -466,11 +474,11 @@
   closure->push(&_version);
   closure->push(&_location);
 }
 
 void ModuleEntry::init_as_archived_entry() {
-  set_archived_reads(write_growable_array(reads()));
+  set_archived_reads(write_growable_array(this, reads()));
 
   _loader_data = nullptr;  // re-init at runtime
   _shared_path_index = FileMapInfo::get_module_shared_path_index(_location);
   if (name() != nullptr) {
     _name = ArchiveBuilder::get_buffered_symbol(_name);
@@ -494,11 +502,16 @@
   assert(_archived_module_index == -1, "must be set exactly once");
   assert(root_oop_index >= 0, "sanity");
 
   _archived_module_index = root_oop_index;
 
-  assert(shared_protection_domain() == nullptr, "never set during -Xshare:dump");
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    OopHandle null_handle;
+    _shared_pd = null_handle;
+  } else {    
+    assert(shared_protection_domain() == nullptr, "never set during -Xshare:dump");
+  }
   // Clear handles and restore at run time. Handles cannot be archived.
   OopHandle null_handle;
   _module = null_handle;
 
   // For verify_archived_module_entries()
@@ -512,18 +525,18 @@
          _num_archived_module_entries, _num_inited_module_entries);
 }
 #endif // PRODUCT
 
 void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) {
-  assert(CDSConfig::is_using_archive(), "runtime only");
+  assert(CDSConfig::is_using_full_module_graph(), "runtime only");
   set_loader_data(loader_data);
   set_reads(restore_growable_array(archived_reads()));
   JFR_ONLY(INIT_ID(this);)
 }
 
 void ModuleEntry::restore_archived_oops(ClassLoaderData* loader_data) {
-  assert(CDSConfig::is_using_archive(), "runtime only");
+  assert(CDSConfig::is_using_full_module_graph(), "runtime only");
   Handle module_handle(Thread::current(), HeapShared::get_root(_archived_module_index, /*clear=*/true));
   assert(module_handle.not_null(), "huh");
   set_module(loader_data->add_handle(module_handle));
 
   // This was cleared to zero during dump time -- we didn't save the value
@@ -540,11 +553,11 @@
     print(&ls);
   }
 }
 
 void ModuleEntry::clear_archived_oops() {
-  assert(CDSConfig::is_using_archive(), "runtime only");
+  assert(CDSConfig::is_using_archive() && !CDSConfig::is_using_full_module_graph(), "runtime only");
   HeapShared::clear_root(_archived_module_index);
 }
 
 static int compare_module_by_name(ModuleEntry* a, ModuleEntry* b) {
   assert(a == b || a->name() != b->name(), "no duplicated names");
@@ -585,21 +598,21 @@
   }
 }
 
 void ModuleEntryTable::load_archived_entries(ClassLoaderData* loader_data,
                                              Array<ModuleEntry*>* archived_modules) {
-  assert(CDSConfig::is_using_archive(), "runtime only");
+  assert(CDSConfig::is_using_full_module_graph(), "runtime only");
 
   for (int i = 0; i < archived_modules->length(); i++) {
     ModuleEntry* archived_entry = archived_modules->at(i);
     archived_entry->load_from_archive(loader_data);
     _table.put(archived_entry->name(), archived_entry);
   }
 }
 
 void ModuleEntryTable::restore_archived_oops(ClassLoaderData* loader_data, Array<ModuleEntry*>* archived_modules) {
-  assert(CDSConfig::is_using_archive(), "runtime only");
+  assert(CDSConfig::is_using_full_module_graph(), "runtime only");
   for (int i = 0; i < archived_modules->length(); i++) {
     ModuleEntry* archived_entry = archived_modules->at(i);
     archived_entry->restore_archived_oops(loader_data);
   }
 }
diff a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp
--- a/src/hotspot/share/classfile/moduleEntry.hpp
+++ b/src/hotspot/share/classfile/moduleEntry.hpp
@@ -196,11 +196,11 @@
   void iterate_symbols(MetaspaceClosure* closure);
   ModuleEntry* allocate_archived_entry() const;
   void init_as_archived_entry();
   static ModuleEntry* get_archived_entry(ModuleEntry* orig_entry);
   bool has_been_archived();
-  static Array<ModuleEntry*>* write_growable_array(GrowableArray<ModuleEntry*>* array);
+  static Array<ModuleEntry*>* write_growable_array(ModuleEntry* module, GrowableArray<ModuleEntry*>* array);
   static GrowableArray<ModuleEntry*>* restore_growable_array(Array<ModuleEntry*>* archived_array);
   void load_from_archive(ClassLoaderData* loader_data);
   void restore_archived_oops(ClassLoaderData* loader_data);
   void clear_archived_oops();
   void update_oops_in_archived_module(int root_oop_index);
diff a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp
--- a/src/hotspot/share/classfile/modules.cpp
+++ b/src/hotspot/share/classfile/modules.cpp
@@ -277,11 +277,10 @@
   }
 }
 
 void Modules::define_module(Handle module, jboolean is_open, jstring version,
                             jstring location, jobjectArray packages, TRAPS) {
-  check_cds_restrictions(CHECK);
   ResourceMark rm(THREAD);
 
   if (module.is_null()) {
     THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null module object");
   }
@@ -299,10 +298,13 @@
   }
 
   // Resolve packages
   objArrayHandle packages_h(THREAD, objArrayOop(JNIHandles::resolve(packages)));
   int num_packages = (packages_h.is_null() ? 0 : packages_h->length());
+  if (strncmp(module_name, "jdk.proxy", 9) != 0) {
+    check_cds_restrictions(Handle(), Handle(), CHECK);
+  }
 
   // Special handling of java.base definition
   if (strcmp(module_name, JAVA_BASE_NAME) == 0) {
     assert(is_open == JNI_FALSE, "java.base module cannot be open");
     define_javabase_module(module, version, location, packages_h, num_packages, CHECK);
@@ -736,18 +738,35 @@
   assert(Arguments::get_property("java.system.class.loader") == nullptr,
            "archived full module should have been disabled if -Djava.system.class.loader is specified");
   ClassLoaderDataShared::restore_java_system_loader_from_archive(system_loader_data);
 }
 
-void Modules::check_cds_restrictions(TRAPS) {
+void Modules::check_cds_restrictions(Handle module1, Handle module2, TRAPS) {
   if (CDSConfig::is_dumping_full_module_graph() && Universe::is_module_initialized()) {
-    THROW_MSG(vmSymbols::java_lang_UnsupportedOperationException(),
-              "During -Xshare:dump, module system cannot be modified after it's initialized");
+    if (CDSConfig::is_dumping_dynamic_proxies() && (is_dynamic_proxy_module(module1) || is_dynamic_proxy_module(module2))) {
+      // The only the we allow is to add or modify the jdk.proxy?? modules that are used for dynamic proxies.
+    } else {
+      THROW_MSG(vmSymbols::java_lang_UnsupportedOperationException(),
+                "During -Xshare:dump, module system cannot be modified after it's initialized");
+    }
   }
 }
+
 #endif // INCLUDE_CDS_JAVA_HEAP
 
+bool Modules::is_dynamic_proxy_module(Handle module) {
+  if (!module.is_null()) {
+    ModuleEntry* module_entry = java_lang_Module::module_entry(module());
+    return is_dynamic_proxy_module(module_entry);
+  }
+  return false;
+}
+
+bool Modules::is_dynamic_proxy_module(ModuleEntry* module_entry) {
+  return (module_entry != nullptr && module_entry->is_named() && module_entry->name()->starts_with("jdk.proxy"));
+}
+
 void Modules::set_bootloader_unnamed_module(Handle module, TRAPS) {
   ResourceMark rm(THREAD);
 
   if (module.is_null()) {
     THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null module object");
@@ -781,11 +800,11 @@
   // Store pointer to the ModuleEntry in the unnamed module's java.lang.Module object.
   java_lang_Module::set_module_entry(module(), unnamed_module);
 }
 
 void Modules::add_module_exports(Handle from_module, jstring package_name, Handle to_module, TRAPS) {
-  check_cds_restrictions(CHECK);
+  check_cds_restrictions(from_module, to_module, CHECK);
 
   if (package_name == nullptr) {
     THROW_MSG(vmSymbols::java_lang_NullPointerException(),
               "package is null");
   }
@@ -849,20 +868,20 @@
 }
 
 
 void Modules::add_module_exports_qualified(Handle from_module, jstring package,
                                            Handle to_module, TRAPS) {
-  check_cds_restrictions(CHECK);
+  check_cds_restrictions(from_module, to_module, CHECK);
   if (to_module.is_null()) {
     THROW_MSG(vmSymbols::java_lang_NullPointerException(),
               "to_module is null");
   }
   add_module_exports(from_module, package, to_module, CHECK);
 }
 
 void Modules::add_reads_module(Handle from_module, Handle to_module, TRAPS) {
-  check_cds_restrictions(CHECK);
+  check_cds_restrictions(from_module, to_module, CHECK);
   if (from_module.is_null()) {
     THROW_MSG(vmSymbols::java_lang_NullPointerException(),
               "from_module is null");
   }
 
@@ -964,11 +983,11 @@
   return nullptr;
 }
 
 // Export package in module to all unnamed modules.
 void Modules::add_module_exports_to_all_unnamed(Handle module, jstring package_name, TRAPS) {
-  check_cds_restrictions(CHECK);
+  check_cds_restrictions(Handle(), module, CHECK);
   if (module.is_null()) {
     THROW_MSG(vmSymbols::java_lang_NullPointerException(),
               "module is null");
   }
   if (package_name == nullptr) {
diff a/src/hotspot/share/classfile/modules.hpp b/src/hotspot/share/classfile/modules.hpp
--- a/src/hotspot/share/classfile/modules.hpp
+++ b/src/hotspot/share/classfile/modules.hpp
@@ -30,11 +30,11 @@
 
 class ModuleEntryTable;
 class Symbol;
 
 class Modules : AllStatic {
-  static void check_cds_restrictions(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
+  static void check_cds_restrictions(Handle module1, Handle module2, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
 
 public:
   // define_module defines a module containing the specified packages. It binds the
   // module to its class loader by creating the ModuleEntry record in the
   // ClassLoader's ModuleEntry table, and creates PackageEntry records in the class
@@ -59,10 +59,14 @@
   static void define_archived_modules(Handle h_platform_loader, Handle h_system_loader,
                                       TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
   static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN;
   static void dump_main_module_name() NOT_CDS_JAVA_HEAP_RETURN;
   static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
+
+  static bool is_dynamic_proxy_module(Handle module);
+  static bool is_dynamic_proxy_module(ModuleEntry* module_entry);
+
   static void dump_addmods_names() NOT_CDS_JAVA_HEAP_RETURN;
   static void serialize_addmods_names(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
   static const char* get_addmods_names_as_sorted_string() NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
 
 #if INCLUDE_CDS_JAVA_HEAP
diff a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp
--- a/src/hotspot/share/classfile/packageEntry.cpp
+++ b/src/hotspot/share/classfile/packageEntry.cpp
@@ -245,11 +245,11 @@
 void PackageEntry::iterate_symbols(MetaspaceClosure* closure) {
   closure->push(&_name);
 }
 
 void PackageEntry::init_as_archived_entry() {
-  Array<ModuleEntry*>* archived_qualified_exports = ModuleEntry::write_growable_array(_qualified_exports);
+  Array<ModuleEntry*>* archived_qualified_exports = ModuleEntry::write_growable_array(module(), _qualified_exports);
 
   _name = ArchiveBuilder::get_buffered_symbol(_name);
   _module = ModuleEntry::get_archived_entry(_module);
   _qualified_exports = (GrowableArray<ModuleEntry*>*)archived_qualified_exports;
   _defined_by_cds_in_class_path = 0;
diff a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp
--- a/src/hotspot/share/classfile/stringTable.cpp
+++ b/src/hotspot/share/classfile/stringTable.cpp
@@ -1053,10 +1053,16 @@
 void StringTable::set_shared_strings_array_index(int root_index) {
   _shared_strings_array_root_index = root_index;
 }
 
 void StringTable::serialize_shared_table_header(SerializeClosure* soc) {
+  if (soc->writing() && !HeapShared::can_write()) {
+    _shared_table.reset();
+    _is_two_dimensional_shared_strings_array = false;
+    _shared_strings_array_root_index = -1;
+  }
+
   _shared_table.serialize_header(soc);
 
   if (soc->writing()) {
     // Sanity. Make sure we don't use the shared table at dump time
     _shared_table.reset();
diff a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp
--- a/src/hotspot/share/classfile/symbolTable.cpp
+++ b/src/hotspot/share/classfile/symbolTable.cpp
@@ -66,16 +66,11 @@
 }
 
 static OffsetCompactHashtable<
   const char*, Symbol*,
   symbol_equals_compact_hashtable_entry
-> _shared_table;
-
-static OffsetCompactHashtable<
-  const char*, Symbol*,
-  symbol_equals_compact_hashtable_entry
-> _dynamic_shared_table;
+> _shared_table, _dynamic_shared_table, _shared_table_for_dumping;
 
 // --------------------------------------------------------------------------
 
 typedef ConcurrentHashTable<SymbolTableConfig, mtSymbol> SymbolTableHash;
 static SymbolTableHash* _local_table = nullptr;
@@ -702,33 +697,28 @@
 }
 
 void SymbolTable::write_to_archive(GrowableArray<Symbol*>* symbols) {
   CompactHashtableWriter writer(int(_items_count), ArchiveBuilder::symbol_stats());
   copy_shared_symbol_table(symbols, &writer);
-  if (CDSConfig::is_dumping_static_archive()) {
-    _shared_table.reset();
-    writer.dump(&_shared_table, "symbol");
-  } else {
-    assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
-    _dynamic_shared_table.reset();
-    writer.dump(&_dynamic_shared_table, "symbol");
-  }
+  _shared_table_for_dumping.reset();
+  writer.dump(&_shared_table_for_dumping, "symbol");
 }
 
 void SymbolTable::serialize_shared_table_header(SerializeClosure* soc,
                                                 bool is_static_archive) {
   OffsetCompactHashtable<const char*, Symbol*, symbol_equals_compact_hashtable_entry> * table;
-  if (is_static_archive) {
-    table = &_shared_table;
+  if (soc->reading()) {
+    if (is_static_archive) {
+      table = &_shared_table;
+    } else {
+      table = &_dynamic_shared_table;
+    }
   } else {
-    table = &_dynamic_shared_table;
+    table = &_shared_table_for_dumping;
   }
+
   table->serialize_header(soc);
-  if (soc->writing()) {
-    // Sanity. Make sure we don't use the shared table at dump time
-    table->reset();
-  }
 }
 #endif //INCLUDE_CDS
 
 // Concurrent work
 void SymbolTable::grow(JavaThread* jt) {
diff a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -34,10 +34,11 @@
 #include "classfile/classLoadInfo.hpp"
 #include "classfile/dictionary.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/klassFactory.hpp"
 #include "classfile/loaderConstraints.hpp"
+#include "classfile/modules.hpp"
 #include "classfile/packageEntry.hpp"
 #include "classfile/placeholders.hpp"
 #include "classfile/protectionDomainCache.hpp"
 #include "classfile/resolutionErrors.hpp"
 #include "classfile/stringTable.hpp"
@@ -158,10 +159,14 @@
     DEBUG_ONLY(
       oop system_loader = get_system_class_loader_impl(CHECK);
       assert(_java_system_loader.resolve() == system_loader, "must be");
     )
   }
+
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    AOTLinkedClassBulkLoader::load_non_javabase_classes(THREAD);
+  }
 }
 
 oop SystemDictionary::get_system_class_loader_impl(TRAPS) {
   JavaValue result(T_OBJECT);
   InstanceKlass* class_loader_klass = vmClasses::ClassLoader_klass();
@@ -1016,11 +1021,15 @@
   ModuleEntry* mod_entry = (pkg_entry == nullptr) ? nullptr : pkg_entry->module();
   bool should_be_in_named_module = (mod_entry != nullptr && mod_entry->is_named());
   bool was_archived_from_named_module = scp_entry->in_named_module();
   bool visible;
 
-  if (was_archived_from_named_module) {
+  if (mod_entry != nullptr && mod_entry->location() == nullptr && mod_entry->is_named()) {
+    // Archived module for dynamic proxies. It's always visible.
+    assert(Modules::is_dynamic_proxy_module(mod_entry), "must be");
+    visible = true;
+  } else if (was_archived_from_named_module) {
     if (should_be_in_named_module) {
       // Is the module loaded from the same location as during dump time?
       visible = mod_entry->shared_path_index() == scp_index;
       if (visible) {
         assert(!mod_entry->is_patched(), "cannot load archived classes for patched module");
@@ -1200,15 +1209,32 @@
 
   // For boot loader, ensure that GetSystemPackage knows that a class in this
   // package was loaded.
   if (loader_data->is_the_null_class_loader_data()) {
     s2 path_index = ik->shared_classpath_index();
-    ik->set_classpath_index(path_index);
+    if (path_index >= 0) { // FIXME ... for lambda form classes
+      ik->set_classpath_index(path_index);
+
+      if (CDSConfig::is_dumping_final_static_archive()) {
+        if (path_index > ClassLoaderExt::max_used_path_index()) {
+          ClassLoaderExt::set_max_used_path_index(path_index);
+        }
+      }
+    }
   }
 
   // notify a class loaded from shared object
   ClassLoadingService::notify_class_loaded(ik, true /* shared class */);
+
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    SystemDictionaryShared::init_dumptime_info(ik);
+    if (SystemDictionary::is_platform_class_loader(loader_data->class_loader())) {
+      ClassLoaderExt::set_has_platform_classes();
+    } else if (SystemDictionary::is_system_class_loader(loader_data->class_loader())) {
+      ClassLoaderExt::set_has_app_classes();
+    }
+  }
 }
 
 #endif // INCLUDE_CDS
 
 InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS) {
@@ -1279,22 +1305,22 @@
     InstanceKlass* k = nullptr;
 
 #if INCLUDE_CDS
     if (CDSConfig::is_using_archive())
     {
-      PerfTraceTime vmtimer(ClassLoader::perf_shared_classload_time());
+      PerfTraceElapsedTime vmtimer(ClassLoader::perf_shared_classload_time());
       InstanceKlass* ik = SystemDictionaryShared::find_builtin_class(class_name);
       if (ik != nullptr && ik->is_shared_boot_class() && !ik->shared_loading_failed()) {
         SharedClassLoadingMark slm(THREAD, ik);
         k = load_shared_class(ik, class_loader, Handle(), nullptr,  pkg_entry, CHECK_NULL);
       }
     }
 #endif
 
     if (k == nullptr) {
       // Use VM class loader
-      PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time());
+      PerfTraceElapsedTime vmtimer(ClassLoader::perf_sys_classload_time());
       k = ClassLoader::load_class(class_name, pkg_entry, search_only_bootloader_append, CHECK_NULL);
     }
 
     // find_or_define_instance_class may return a different InstanceKlass
     if (k != nullptr) {
diff a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -32,10 +32,11 @@
 #include "cds/classListWriter.hpp"
 #include "cds/dumpTimeClassInfo.inline.hpp"
 #include "cds/dynamicArchive.hpp"
 #include "cds/filemap.hpp"
 #include "cds/heapShared.hpp"
+#include "cds/lambdaFormInvokers.inline.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "cds/runTimeClassInfo.hpp"
 #include "classfile/classFileStream.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderData.inline.hpp"
@@ -61,10 +62,12 @@
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/compressedKlass.inline.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/methodData.hpp"
+#include "oops/trainingData.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oopHandle.inline.hpp"
 #include "oops/typeArrayOop.inline.hpp"
@@ -80,10 +83,12 @@
 SystemDictionaryShared::ArchiveInfo SystemDictionaryShared::_dynamic_archive;
 
 DumpTimeSharedClassTable* SystemDictionaryShared::_dumptime_table = nullptr;
 DumpTimeLambdaProxyClassDictionary* SystemDictionaryShared::_dumptime_lambda_proxy_class_dictionary = nullptr;
 
+static bool _ignore_new_classes = false;
+
 // Used by NoClassLoadingMark
 DEBUG_ONLY(bool SystemDictionaryShared::_class_loading_may_happen = true;)
 
 #ifdef ASSERT
 static void check_klass_after_loading(const Klass* k) {
@@ -103,12 +108,15 @@
   if (ik != nullptr && !ik->shared_loading_failed()) {
     if ((SystemDictionary::is_system_class_loader(class_loader()) && ik->is_shared_app_class())  ||
         (SystemDictionary::is_platform_class_loader(class_loader()) && ik->is_shared_platform_class())) {
       SharedClassLoadingMark slm(THREAD, ik);
       PackageEntry* pkg_entry = CDSProtectionDomain::get_package_entry_from_class(ik, class_loader);
-      Handle protection_domain =
-        CDSProtectionDomain::init_security_info(class_loader, ik, pkg_entry, CHECK_NULL);
+      Handle protection_domain;
+      if (!class_name->starts_with("jdk/proxy")) // java/lang/reflect/Proxy$ProxyBuilder defines the proxy classes with a null protection domain.
+      {
+        protection_domain = CDSProtectionDomain::init_security_info(class_loader, ik, pkg_entry, CHECK_NULL);
+      }
       return load_shared_class(ik, class_loader, protection_domain, nullptr, pkg_entry, THREAD);
     }
   }
   return nullptr;
 }
@@ -191,17 +199,17 @@
 
 // Guaranteed to return non-null value for non-shared classes.
 // k must not be a shared class.
 DumpTimeClassInfo* SystemDictionaryShared::get_info(InstanceKlass* k) {
   MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
-  assert(!k->is_shared(), "sanity");
+//assert(!k->is_shared(), "sanity"); // FIXME new workflow
   return get_info_locked(k);
 }
 
 DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) {
   assert_lock_strong(DumpTimeTable_lock);
-  assert(!k->is_shared(), "sanity");
+//assert(!k->is_shared(), "sanity"); // FIXME new workflow
   DumpTimeClassInfo* info = _dumptime_table->get_info(k);
   assert(info != nullptr, "must be");
   return info;
 }
 
@@ -213,11 +221,11 @@
     info->set_is_required_hidden_class();
   }
 }
 
 bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
-  if (MetaspaceShared::is_in_shared_metaspace(k)) {
+  if (!CDSConfig::is_dumping_final_static_archive() && MetaspaceShared::is_in_shared_metaspace(k)) {
     // We have reached a super type that's already in the base archive. Treat it
     // as "not excluded".
     assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
     return false;
   }
@@ -279,11 +287,21 @@
   } else {
     return false;
   }
 }
 
+void SystemDictionaryShared::ignore_new_classes() {
+  _ignore_new_classes = true;
+}
+
+
 bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
+  if (CDSConfig::is_dumping_final_static_archive() && k->is_shared_unregistered_class()
+      && k->is_shared()) {
+    return false; // Do not exclude: unregistered classes are passed from preimage to final image.
+  }
+
   if (k->is_in_error_state()) {
     return warn_excluded(k, "In error state");
   }
   if (k->is_scratch_class()) {
     return warn_excluded(k, "A scratch class");
@@ -319,26 +337,30 @@
   if (is_jfr_event_class(k)) {
     // We cannot include JFR event classes because they need runtime-specific
     // instrumentation in order to work with -XX:FlightRecorderOptions:retransform=false.
     // There are only a small number of these classes, so it's not worthwhile to
     // support them and make CDS more complicated.
-    return warn_excluded(k, "JFR event class");
+    if (!CDSConfig::is_dumping_reflection_data()) { // FIXME: !!! HACK !!!
+      return warn_excluded(k, "JFR event class");
+    }
   }
 
-  if (!k->is_linked()) {
-    if (has_class_failed_verification(k)) {
-      return warn_excluded(k, "Failed verification");
-    }
-  } else {
-    if (!k->can_be_verified_at_dumptime()) {
-      // We have an old class that has been linked (e.g., it's been executed during
-      // dump time). This class has been verified using the old verifier, which
-      // doesn't save the verification constraints, so check_verification_constraints()
-      // won't work at runtime.
-      // As a result, we cannot store this class. It must be loaded and fully verified
-      // at runtime.
-      return warn_excluded(k, "Old class has been linked");
+  if (!CDSConfig::preserve_all_dumptime_verification_states(k)) {
+    if (!k->is_linked()) {
+      if (has_class_failed_verification(k)) {
+        return warn_excluded(k, "Failed verification");
+      }
+    } else {
+      if (!k->can_be_verified_at_dumptime()) {
+        // We have an old class that has been linked (e.g., it's been executed during
+        // dump time). This class has been verified using the old verifier, which
+        // doesn't save the verification constraints, so check_verification_constraints()
+        // won't work at runtime.
+        // As a result, we cannot store this class. It must be loaded and fully verified
+        // at runtime.
+        return warn_excluded(k, "Old class has been linked");
+      }
     }
   }
 
   if (k->is_hidden() && !should_hidden_class_be_archived(k)) {
     log_info(cds)("Skipping %s: Hidden class", k->name()->as_C_string());
@@ -361,10 +383,16 @@
       log_warning(cds)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
       return true;
     }
   }
 
+  if (ClassLoaderExt::should_be_excluded(k)) {
+    ResourceMark rm;
+    log_info(cds)("Skipping %s: excluded via -XX:CacheOnlyClassesIn", k->name()->as_C_string());
+    return true;
+  }
+
   return false; // false == k should NOT be excluded
 }
 
 bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) {
   oop class_loader = loader_data->class_loader();
@@ -537,11 +565,18 @@
 }
 
 void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) {
   MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
   assert(SystemDictionaryShared::class_loading_may_happen(), "sanity");
-  _dumptime_table->allocate_info(k);
+  DumpTimeClassInfo* info = _dumptime_table->allocate_info(k);
+  if (_ignore_new_classes) {
+    if (!LambdaFormInvokers::may_be_regenerated_class(k->name())) {
+      ResourceMark rm;
+      log_debug(cds)("Skipping %s: Class loaded for lambda form invoker regeneration", k->name()->as_C_string());
+      info->set_excluded();
+    }
+  }
 }
 
 void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
   MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
   _dumptime_table->remove(k);
@@ -807,10 +842,12 @@
   };
   _dumptime_table->iterate_all_live_classes(exclude_remaining_hidden);
   _dumptime_table->update_counts();
 
   cleanup_lambda_proxy_class_dictionary();
+
+  TrainingData::cleanup_training_data();
 }
 
 bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
   assert(!class_loading_may_happen(), "class loading must be disabled");
   assert_lock_strong(DumpTimeTable_lock);
@@ -846,11 +883,14 @@
 
 void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) {
   assert_lock_strong(DumpTimeTable_lock);
 
   auto do_klass = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
-    if (k->is_loader_alive() && !info.is_excluded()) {
+    if (CDSConfig::is_dumping_final_static_archive() && !k->is_loaded()) {
+      assert(k->is_shared_unregistered_class(), "must be");
+      info.metaspace_pointers_do(it);
+    } else if (k->is_loader_alive() && !info.is_excluded()) {
       info.metaspace_pointers_do(it);
     }
   };
   _dumptime_table->iterate_all_live_classes(do_klass);
 
@@ -864,10 +904,21 @@
 }
 
 bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
          Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
   assert(CDSConfig::is_dumping_archive(), "sanity");
+  if (CDSConfig::is_dumping_dynamic_archive() && k->is_shared()) {
+    // k is a new class in the static archive, but one of its supertypes is an old class, so k wasn't
+    // verified during dump time. No need to record constraints as k won't be included in the dynamic archive.
+    return false;
+  }
+  if (CDSConfig::is_dumping_aot_linked_classes() && is_builtin(k)) {
+    // There's no need to save verification constraints
+    // TODO -- double check the logic before integrating into mainline!!
+    return false;
+  }
+
   DumpTimeClassInfo* info = get_info(k);
   info->add_verification_constraint(k, name, from_name, from_field_is_protected,
                                     from_is_array, from_is_object);
 
   if (CDSConfig::is_dumping_dynamic_archive()) {
@@ -918,10 +969,14 @@
   if (CDSConfig::is_dumping_invokedynamic()) {
     // The lambda proxy classes will be stored as part of aot-resolved constant pool entries.
     // There's no need to remember them in a separate table.
     return;
   }
+  if (CDSConfig::is_dumping_preimage_static_archive() || CDSConfig::is_dumping_final_static_archive()) {
+    // TODO: not supported in new workflow
+    return;
+  }
 
   assert(caller_ik->class_loader() == lambda_ik->class_loader(), "mismatched class loader");
   assert(caller_ik->class_loader_data() == lambda_ik->class_loader_data(), "mismatched class loader data");
   assert(java_lang_Class::class_data(lambda_ik->java_mirror()) == nullptr, "must not have class data");
 
@@ -955,10 +1010,14 @@
                                                                      Symbol* invoked_name,
                                                                      Symbol* invoked_type,
                                                                      Symbol* method_type,
                                                                      Method* member_method,
                                                                      Symbol* instantiated_method_type) {
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    return nullptr;
+  }
+
   if (!caller_ik->is_shared()     ||
       !invoked_name->is_shared()  ||
       !invoked_type->is_shared()  ||
       !method_type->is_shared()   ||
       !member_method->is_shared() ||
@@ -1014,11 +1073,11 @@
       prev_klass->set_next_link(nullptr);
       proxy_klass = curr_klass;
       proxy_klass->clear_lambda_proxy_is_available();
       if (log_is_enabled(Debug, cds)) {
         ResourceMark rm;
-        log_debug(cds)("Loaded lambda proxy: %s ", proxy_klass->external_name());
+        log_debug(cds)("Loaded lambda proxy: " PTR_FORMAT " %s ", p2i(proxy_klass), proxy_klass->external_name());
       }
     }
   }
   return proxy_klass;
 }
@@ -1073,11 +1132,11 @@
   return loaded_lambda;
 }
 
 void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
                                                             TRAPS) {
-  assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
+  //assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
   RunTimeClassInfo* record = RunTimeClassInfo::get_for(klass);
 
   int length = record->num_verifier_constraints();
   if (length > 0) {
     for (int i = 0; i < length; i++) {
@@ -1183,11 +1242,11 @@
 }
 
 // returns true IFF there's no need to re-initialize the i/v-tables for klass for
 // the purpose of checking class loader constraints.
 bool SystemDictionaryShared::check_linking_constraints(Thread* current, InstanceKlass* klass) {
-  assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
+  //assert(!CDSConfig::is_dumping_static_archive() && CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
   LogTarget(Info, class, loader, constraints) log;
   if (klass->is_shared_boot_class()) {
     // No class loader constraint check performed for boot classes.
     return true;
   }
@@ -1316,11 +1375,11 @@
 
   return total_size;
 }
 
 unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) {
-  if (ArchiveBuilder::is_active()) {
+  if (ArchiveBuilder::is_active() && ArchiveBuilder::current()->is_in_buffer_space(ptr)) {
     uintx offset = ArchiveBuilder::current()->any_to_offset(ptr);
     unsigned int hash = primitive_hash<uintx>(offset);
     DEBUG_ONLY({
         if (MetaspaceObj::is_shared((const MetaspaceObj*)ptr)) {
           assert(hash == SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr), "must be");
@@ -1481,14 +1540,11 @@
   unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(name);
   const RunTimeClassInfo* record = nullptr;
   if (DynamicArchive::is_mapped()) {
     // Use the regenerated holder classes in the dynamic archive as they
     // have more methods than those in the base archive.
-    if (name == vmSymbols::java_lang_invoke_Invokers_Holder() ||
-        name == vmSymbols::java_lang_invoke_DirectMethodHandle_Holder() ||
-        name == vmSymbols::java_lang_invoke_LambdaForm_Holder() ||
-        name == vmSymbols::java_lang_invoke_DelegatingMethodHandle_Holder()) {
+    if (LambdaFormInvokers::may_be_regenerated_class(name)) {
       record = dynamic_dict->lookup(name, hash, 0);
       if (record != nullptr) {
         return record;
       }
     }
@@ -1529,11 +1585,11 @@
   assert(CDSConfig::is_dumping_static_archive(), "class ID is used only for static dump (from classlist)");
   DumpTimeClassInfo* info = get_info(k);
   info->_id = id;
 }
 
-static const char* class_loader_name_for_shared(Klass* k) {
+const char* SystemDictionaryShared::class_loader_name_for_shared(Klass* k) {
   assert(k != nullptr, "Sanity");
   assert(k->is_shared(), "Must be");
   assert(k->is_instance_klass(), "Must be");
   InstanceKlass* ik = InstanceKlass::cast(k);
   if (ik->is_shared_boot_class()) {
@@ -1556,11 +1612,11 @@
   SharedDictionaryPrinter(outputStream* st) : _st(st), _index(0) {}
 
   void do_value(const RunTimeClassInfo* record) {
     ResourceMark rm;
     _st->print_cr("%4d: %s %s", _index++, record->klass()->external_name(),
-        class_loader_name_for_shared(record->klass()));
+        SystemDictionaryShared::class_loader_name_for_shared(record->klass()));
     if (record->klass()->array_klasses() != nullptr) {
       record->klass()->array_klasses()->cds_print_value_on(_st);
       _st->cr();
     }
   }
@@ -1577,11 +1633,11 @@
     if (record->proxy_klass_head()->lambda_proxy_is_available()) {
       ResourceMark rm;
       Klass* k = record->proxy_klass_head();
       while (k != nullptr) {
         _st->print_cr("%4d: %s %s", _index++, k->external_name(),
-                      class_loader_name_for_shared(k));
+                      SystemDictionaryShared::class_loader_name_for_shared(k));
         k = k->next_link();
       }
     }
   }
 };
@@ -1670,5 +1726,59 @@
 void SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary() {
   assert_lock_strong(DumpTimeTable_lock);
   CleanupDumpTimeLambdaProxyClassTable cleanup_proxy_classes;
   _dumptime_lambda_proxy_class_dictionary->unlink(&cleanup_proxy_classes);
 }
+
+void SystemDictionaryShared::create_loader_positive_lookup_cache(TRAPS) {
+  GrowableArray<InstanceKlass*> shared_classes_list;
+  {
+    // With static dumping, we have only a single Java thread (see JVM_StartThread) so
+    // no no other threads should be loading classes. Otherwise, the code below may miss some
+    // classes that are loaded concurrently.
+    assert(CDSConfig::is_dumping_static_archive(), "no other threads should be loading classes");
+
+    MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
+    _dumptime_table->iterate_all_classes_in_builtin_loaders([&](InstanceKlass* k, DumpTimeClassInfo& info) {
+        if (!k->is_hidden() && !check_for_exclusion(k, &info)) {
+          shared_classes_list.append(k);
+        }
+      }
+    );
+  }
+
+  InstanceKlass* ik = vmClasses::Class_klass();
+  objArrayOop r = oopFactory::new_objArray(ik, shared_classes_list.length(), CHECK);
+  objArrayHandle array_h(THREAD, r);
+
+  for (int i = 0; i < shared_classes_list.length(); i++) {
+    oop mirror = shared_classes_list.at(i)->java_mirror();
+    Handle mirror_h(THREAD, mirror);
+    array_h->obj_at_put(i, mirror_h());
+  }
+
+  TempNewSymbol method = SymbolTable::new_symbol("generatePositiveLookupCache");
+  TempNewSymbol signature = SymbolTable::new_symbol("([Ljava/lang/Class;)V");
+
+  JavaCallArguments args(Handle(THREAD, SystemDictionary::java_system_loader()));
+  args.push_oop(array_h);
+  JavaValue result(T_VOID);
+  JavaCalls::call_virtual(&result,
+                          vmClasses::jdk_internal_loader_ClassLoaders_AppClassLoader_klass(),
+                          method,
+                          signature,
+                          &args,
+                          CHECK);
+
+  if (HAS_PENDING_EXCEPTION) {
+    Handle exc_handle(THREAD, PENDING_EXCEPTION);
+    CLEAR_PENDING_EXCEPTION;
+    ResourceMark rm(THREAD);
+
+    log_warning(cds)("Exception during AppClassLoader::generatePositiveLookupCache() call");
+    LogStreamHandle(Debug, cds) log;
+    if (log.is_enabled()) {
+      java_lang_Throwable::print_stack_trace(exc_handle, &log);
+    }
+    return;
+  }
+}
diff a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp
--- a/src/hotspot/share/classfile/systemDictionaryShared.hpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp
@@ -33,10 +33,11 @@
 #include "classfile/classLoaderData.hpp"
 #include "classfile/packageEntry.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "oops/klass.hpp"
 #include "oops/oopHandle.hpp"
+#include "oops/trainingData.hpp"
 
 
 /*===============================================================================
 
     Handling of the classes in the AppCDS archive
@@ -181,33 +182,36 @@
                                  Handle class_loader,
                                  Handle protection_domain,
                                  const ClassFileStream* cfs,
                                  TRAPS);
 
-  // Guaranteed to return non-null value for non-shared classes.
-  // k must not be a shared class.
-  static DumpTimeClassInfo* get_info(InstanceKlass* k);
-  static DumpTimeClassInfo* get_info_locked(InstanceKlass* k);
 
   static void find_all_archivable_classes_impl();
   static void write_dictionary(RunTimeSharedDictionary* dictionary,
                                bool is_builtin);
   static void write_lambda_proxy_class_dictionary(LambdaProxyClassDictionary* dictionary);
   static void cleanup_lambda_proxy_class_dictionary();
   static void reset_registered_lambda_proxy_class(InstanceKlass* ik);
-  static bool is_jfr_event_class(InstanceKlass *k);
   static bool is_registered_lambda_proxy_class(InstanceKlass* ik);
   static bool check_for_exclusion_impl(InstanceKlass* k);
   static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
-  static bool has_been_redefined(InstanceKlass* k);
   static InstanceKlass* retrieve_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info) NOT_CDS_RETURN_(nullptr);
   static void scan_constant_pool(InstanceKlass* k);
   DEBUG_ONLY(static bool _class_loading_may_happen;)
 
 public:
+  // Guaranteed to return non-null value for non-shared classes.
+  // k must not be a shared class.
+  static DumpTimeClassInfo* get_info(InstanceKlass* k);
+  static DumpTimeClassInfo* get_info_locked(InstanceKlass* k);
+  static DumpTimeSharedClassTable* dumptime_table() { return _dumptime_table; }
+
   static bool should_hidden_class_be_archived(InstanceKlass* k);
   static void mark_required_hidden_class(InstanceKlass* k);
+  static bool has_been_redefined(InstanceKlass* k);
+  static bool is_jfr_event_class(InstanceKlass *k);
+
   static bool is_hidden_lambda_proxy(InstanceKlass* ik);
   static bool is_early_klass(InstanceKlass* k);   // Was k loaded while JvmtiExport::is_early_phase()==true
   static bool has_archived_enum_objs(InstanceKlass* ik);
   static void set_has_archived_enum_objs(InstanceKlass* ik);
 
@@ -234,10 +238,14 @@
 
   static void initialize() NOT_CDS_RETURN;
   static void init_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
   static void handle_class_unloading(InstanceKlass* k) NOT_CDS_RETURN;
 
+  static bool can_be_preinited(InstanceKlass* ik);
+  static bool can_be_preinited_locked(InstanceKlass* ik);
+  static void reset_preinit_check();
+
   static Dictionary* boot_loader_dictionary() {
     return ClassLoaderData::the_null_class_loader_data()->dictionary();
   }
 
   static void update_shared_entry(InstanceKlass* klass, int id);
@@ -284,11 +292,11 @@
   static InstanceKlass* prepare_shared_lambda_proxy_class(InstanceKlass* lambda_ik,
                                                           InstanceKlass* caller_ik, TRAPS) NOT_CDS_RETURN_(nullptr);
   static bool check_linking_constraints(Thread* current, InstanceKlass* klass) NOT_CDS_RETURN_(false);
   static void record_linking_constraint(Symbol* name, InstanceKlass* klass,
                                      Handle loader1, Handle loader2) NOT_CDS_RETURN;
-  static bool is_builtin(InstanceKlass* k) {
+  static bool is_builtin(const InstanceKlass* k) {
     return (k->shared_classpath_index() != UNREGISTERED_INDEX);
   }
   static bool add_unregistered_class(Thread* current, InstanceKlass* k);
 
   static void find_all_archivable_classes();
@@ -301,20 +309,24 @@
   static bool warn_excluded(InstanceKlass* k, const char* reason);
   static void dumptime_classes_do(class MetaspaceClosure* it);
   static size_t estimate_size_for_archive();
   static void write_to_archive(bool is_static_archive = true);
   static void adjust_lambda_proxy_class_dictionary();
+
   static void serialize_dictionary_headers(class SerializeClosure* soc,
                                            bool is_static_archive = true);
   static void serialize_vm_classes(class SerializeClosure* soc);
   static void print() { return print_on(tty); }
   static void print_on(outputStream* st) NOT_CDS_RETURN;
   static void print_shared_archive(outputStream* st, bool is_static = true) NOT_CDS_RETURN;
   static void print_table_statistics(outputStream* st) NOT_CDS_RETURN;
   static bool is_dumptime_table_empty() NOT_CDS_RETURN_(true);
   static bool is_supported_invokedynamic(BootstrapInfo* bsi) NOT_CDS_RETURN_(false);
   DEBUG_ONLY(static bool class_loading_may_happen() {return _class_loading_may_happen;})
+  // Do not archive any new InstanceKlasses that are loaded after this method is called.
+  // This avoids polluting the archive with classes that are only used by GenerateJLIClassesHelper.
+  static void ignore_new_classes();
 
 #ifdef ASSERT
   // This object marks a critical period when writing the CDS archive. During this
   // period, the JVM must not load any new classes, so as to avoid adding new
   // items in the SystemDictionaryShared::_dumptime_table.
@@ -337,8 +349,10 @@
     uintx offset = uintx(ptr) - uintx(SharedBaseAddress);
     return primitive_hash<uintx>(offset);
   }
 
   static unsigned int hash_for_shared_dictionary(address ptr);
+  static const char* class_loader_name_for_shared(Klass* k);
+  static void create_loader_positive_lookup_cache(TRAPS);
 };
 
 #endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
diff a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -308,11 +308,13 @@
                                                                                                   \
   template(jdk_internal_vm_annotation_ChangesCurrentThread_signature,  "Ljdk/internal/vm/annotation/ChangesCurrentThread;")  \
   template(jdk_internal_vm_annotation_JvmtiHideEvents_signature,       "Ljdk/internal/vm/annotation/JvmtiHideEvents;")  \
   template(jdk_internal_vm_annotation_JvmtiMountTransition_signature,  "Ljdk/internal/vm/annotation/JvmtiMountTransition;")  \
                                                                                                   \
-  template(java_lang_ref_SoftReference_signature,         "Ljava/lang/ref/SoftReference;")        \
+  template(class_ReflectionData_signature,         "Ljava/lang/Class$ReflectionData;")            \
+  template(encodeReflectionData_name,              "encodeReflectionData")                        \
+  template(generateReflectionData_name,            "generateReflectionData")                      \
                                                                                                   \
   /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */                                   \
   template(java_lang_invoke_CallSite,                 "java/lang/invoke/CallSite")                \
   template(java_lang_invoke_ConstantCallSite,         "java/lang/invoke/ConstantCallSite")        \
   template(java_lang_invoke_DirectMethodHandle,       "java/lang/invoke/DirectMethodHandle")      \
diff a/src/hotspot/share/code/SCCache.cpp b/src/hotspot/share/code/SCCache.cpp
--- /dev/null
+++ b/src/hotspot/share/code/SCCache.cpp
@@ -0,0 +1,4288 @@
+/*
+ * Copyright (c) 2023, 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "asm/macroAssembler.hpp"
+#include "cds/cdsAccess.hpp"
+#include "cds/cdsConfig.hpp"
+#include "cds/heapShared.hpp"
+#include "cds/metaspaceShared.hpp"
+#include "ci/ciConstant.hpp"
+#include "ci/ciEnv.hpp"
+#include "ci/ciField.hpp"
+#include "ci/ciMethod.hpp"
+#include "ci/ciMethodData.hpp"
+#include "ci/ciObject.hpp"
+#include "ci/ciUtilities.inline.hpp"
+#include "classfile/javaAssertions.hpp"
+#include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmClasses.hpp"
+#include "classfile/vmIntrinsics.hpp"
+#include "code/codeBlob.hpp"
+#include "code/codeCache.hpp"
+#include "code/oopRecorder.inline.hpp"
+#include "code/SCCache.hpp"
+#include "compiler/abstractCompiler.hpp"
+#include "compiler/compilationPolicy.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/compileTask.hpp"
+#include "gc/g1/g1BarrierSetRuntime.hpp"
+#include "gc/shared/gcConfig.hpp"
+#include "logging/log.hpp"
+#include "memory/universe.hpp"
+#include "oops/klass.inline.hpp"
+#include "oops/method.inline.hpp"
+#include "oops/trainingData.hpp"
+#include "prims/jvmtiThreadState.hpp"
+#include "runtime/atomic.hpp"
+#include "runtime/flags/flagSetting.hpp"
+#include "runtime/globals_extension.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/java.hpp"
+#include "runtime/jniHandles.inline.hpp"
+#include "runtime/os.inline.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/stubCodeGenerator.hpp"
+#include "runtime/stubRoutines.hpp"
+#include "runtime/timerTrace.hpp"
+#include "runtime/threadIdentifier.hpp"
+#include "utilities/ostream.hpp"
+#include "utilities/spinYield.hpp"
+#ifdef COMPILER1
+#include "c1/c1_Runtime1.hpp"
+#include "c1/c1_LIRAssembler.hpp"
+#include "gc/shared/c1/barrierSetC1.hpp"
+#include "gc/g1/c1/g1BarrierSetC1.hpp"
+#if INCLUDE_SHENANDOAHGC
+#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp"
+#endif
+#include "gc/z/c1/zBarrierSetC1.hpp"
+#endif
+#ifdef COMPILER2
+#include "opto/runtime.hpp"
+#endif
+#if INCLUDE_JVMCI
+#include "jvmci/jvmci.hpp"
+#endif
+#if INCLUDE_SHENANDOAHGC
+#include "gc/shenandoah/shenandoahRuntime.hpp"
+#endif
+
+#include <sys/stat.h>
+#include <errno.h>
+
+#ifndef O_BINARY       // if defined (Win32) use binary files.
+#define O_BINARY 0     // otherwise do nothing.
+#endif
+
+static elapsedTimer _t_totalLoad;
+static elapsedTimer _t_totalRegister;
+static elapsedTimer _t_totalFind;
+static elapsedTimer _t_totalStore;
+
+SCCache* SCCache::_cache = nullptr;
+
+static bool enable_timers() {
+  return CITime || log_is_enabled(Info, init);
+}
+
+static void exit_vm_on_load_failure() {
+  // Treat SCC warnings as error when RequireSharedSpaces is on.
+  if (RequireSharedSpaces) {
+    vm_exit_during_initialization("Unable to used startup cached code.", nullptr);
+  }
+}
+
+static void exit_vm_on_store_failure() {
+  // Treat SCC warnings as error when RequireSharedSpaces is on.
+  if (RequireSharedSpaces) {
+    tty->print_cr("Unable to create startup cached code.");
+    // Failure during AOT code caching, we don't want to dump core
+    vm_abort(false);
+  }
+}
+void SCCache::initialize() {
+  if (LoadCachedCode && !UseSharedSpaces) {
+    return;
+  }
+  if (StoreCachedCode || LoadCachedCode) {
+    if (FLAG_IS_DEFAULT(ClassInitBarrierMode)) {
+      FLAG_SET_DEFAULT(ClassInitBarrierMode, 1);
+    }
+  } else if (ClassInitBarrierMode > 0) {
+    log_info(scc, init)("Set ClassInitBarrierMode to 0 because StoreCachedCode and LoadCachedCode are false.");
+    FLAG_SET_DEFAULT(ClassInitBarrierMode, 0);
+  }
+  if ((LoadCachedCode || StoreCachedCode) && CachedCodeFile != nullptr) {
+    const int len = (int)strlen(CachedCodeFile);
+    // cache file path
+    char* path  = NEW_C_HEAP_ARRAY(char, len+1, mtCode);
+    memcpy(path, CachedCodeFile, len);
+    path[len] = '\0';
+    if (!open_cache(path)) {
+      exit_vm_on_load_failure();
+      return;
+    }
+    if (StoreCachedCode) {
+      FLAG_SET_DEFAULT(FoldStableValues, false);
+      FLAG_SET_DEFAULT(ForceUnreachable, true);
+    }
+    FLAG_SET_DEFAULT(DelayCompilerStubsGeneration, false);
+  }
+}
+
+void SCCache::init2() {
+  if (!is_on()) {
+    return;
+  }
+  // After Universe initialized
+  BarrierSet* bs = BarrierSet::barrier_set();
+  if (bs->is_a(BarrierSet::CardTableBarrierSet)) {
+    address byte_map_base = ci_card_table_address_as<address>();
+    if (is_on_for_write() && !external_word_Relocation::can_be_relocated(byte_map_base)) {
+      // Bail out since we can't encode card table base address with relocation
+      log_warning(scc, init)("Can't create Startup Code Cache because card table base address is not relocatable: " INTPTR_FORMAT, p2i(byte_map_base));
+      close();
+      exit_vm_on_load_failure();
+    }
+  }
+  // initialize aot runtime constants as appropriate to this runtime
+  AOTRuntimeConstants::initialize_from_runtime();
+
+  if (!verify_vm_config()) {
+    close();
+    exit_vm_on_load_failure();
+  }
+}
+
+void SCCache::print_timers_on(outputStream* st) {
+  if (LoadCachedCode) {
+    st->print_cr ("    SC Load Time:         %7.3f s", _t_totalLoad.seconds());
+    st->print_cr ("      nmethod register:     %7.3f s", _t_totalRegister.seconds());
+    st->print_cr ("      find cached code:     %7.3f s", _t_totalFind.seconds());
+  }
+  if (StoreCachedCode) {
+    st->print_cr ("    SC Store Time:        %7.3f s", _t_totalStore.seconds());
+  }
+}
+
+bool SCCache::is_C3_on() {
+#if INCLUDE_JVMCI
+  if (UseJVMCICompiler) {
+    return (StoreCachedCode || LoadCachedCode) && UseC2asC3;
+  }
+#endif
+  return false;
+}
+
+bool SCCache::is_code_load_thread_on() {
+  return UseCodeLoadThread && LoadCachedCode;
+}
+
+bool SCCache::gen_preload_code(ciMethod* m, int entry_bci) {
+  VM_ENTRY_MARK;
+  return (entry_bci == InvocationEntryBci) && is_on() && _cache->gen_preload_code() &&
+         CDSAccess::can_generate_cached_code(m->get_Method());
+}
+
+static void print_helper(nmethod* nm, outputStream* st) {
+  SCCache::iterate([&](SCCEntry* e) {
+    if (e->method() == nm->method()) {
+      ResourceMark rm;
+      stringStream ss;
+      ss.print("A%s%d", (e->for_preload() ? "P" : ""), e->comp_level());
+      if (e->decompile() > 0) {
+        ss.print("+D%d", e->decompile());
+      }
+      ss.print("[%s%s%s]",
+               (e->is_loaded()   ? "L" : ""),
+               (e->load_fail()   ? "F" : ""),
+               (e->not_entrant() ? "I" : ""));
+      ss.print("#%d", e->comp_id());
+
+      st->print(" %s", ss.freeze());
+    }
+  });
+}
+
+void SCCache::close() {
+  if (is_on()) {
+    if (SCCache::is_on_for_read()) {
+      LogStreamHandle(Info, init) log;
+      if (log.is_enabled()) {
+        log.print_cr("Startup Code Cache statistics (when closed): ");
+        SCCache::print_statistics_on(&log);
+        log.cr();
+        SCCache::print_timers_on(&log);
+
+        LogStreamHandle(Info, scc, init) log1;
+        if (log1.is_enabled()) {
+          SCCache::print_unused_entries_on(&log1);
+        }
+
+        LogStreamHandle(Info, scc, codecache) info_scc;
+        if (info_scc.is_enabled()) {
+          NMethodIterator iter(NMethodIterator::all);
+          while (iter.next()) {
+            nmethod* nm = iter.method();
+            if (nm->is_in_use() && !nm->is_native_method() && !nm->is_osr_method()) {
+              info_scc.print("%5d:%c%c%c%d:", nm->compile_id(),
+                             (nm->method()->is_shared() ? 'S' : ' '),
+                             (nm->is_scc() ? 'A' : ' '),
+                             (nm->preloaded() ? 'P' : ' '),
+                             nm->comp_level());
+              print_helper(nm, &info_scc);
+              info_scc.print(": ");
+              CompileTask::print(&info_scc, nm, nullptr, true /*short_form*/);
+
+              LogStreamHandle(Debug, scc, codecache) debug_scc;
+              if (debug_scc.is_enabled()) {
+                MethodTrainingData* mtd = MethodTrainingData::lookup_for(nm->method());
+                if (mtd != nullptr) {
+                  mtd->iterate_all_compiles([&](CompileTrainingData* ctd) {
+                    debug_scc.print("     CTD: "); ctd->print_on(&debug_scc); debug_scc.cr();
+                  });
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    delete _cache; // Free memory
+    _cache = nullptr;
+  }
+}
+
+void SCCache::invalidate(SCCEntry* entry) {
+  // This could be concurent execution
+  if (entry != nullptr && is_on()) { // Request could come after cache is closed.
+    _cache->invalidate_entry(entry);
+  }
+}
+
+bool SCCache::is_loaded(SCCEntry* entry) {
+  if (is_on() && _cache->cache_buffer() != nullptr) {
+    return (uint)((char*)entry - _cache->cache_buffer()) < _cache->load_size();
+  }
+  return false;
+}
+
+void SCCache::preload_code(JavaThread* thread) {
+  if ((ClassInitBarrierMode == 0) || !is_on_for_read()) {
+    return;
+  }
+  if ((DisableCachedCode & (1 << 3)) != 0) {
+    return; // no preloaded code (level 5);
+  }
+  _cache->preload_startup_code(thread);
+}
+
+SCCEntry* SCCache::find_code_entry(const methodHandle& method, uint comp_level) {
+  switch (comp_level) {
+    case CompLevel_simple:
+      if ((DisableCachedCode & (1 << 0)) != 0) {
+        return nullptr;
+      }
+      break;
+    case CompLevel_limited_profile:
+      if ((DisableCachedCode & (1 << 1)) != 0) {
+        return nullptr;
+      }
+      break;
+    case CompLevel_full_optimization:
+      if ((DisableCachedCode & (1 << 2)) != 0) {
+        return nullptr;
+      }
+      break;
+
+    default: return nullptr; // Level 1, 2, and 4 only
+  }
+  TraceTime t1("SC total find code time", &_t_totalFind, enable_timers(), false);
+  if (is_on() && _cache->cache_buffer() != nullptr) {
+    MethodData* md = method->method_data();
+    uint decomp = (md == nullptr) ? 0 : md->decompile_count();
+
+    ResourceMark rm;
+    const char* target_name = method->name_and_sig_as_C_string();
+    uint hash = java_lang_String::hash_code((const jbyte*)target_name, (int)strlen(target_name));
+    SCCEntry* entry = _cache->find_entry(SCCEntry::Code, hash, comp_level, decomp);
+    if (entry == nullptr) {
+      log_info(scc, nmethod)("Missing entry for '%s' (comp_level %d, decomp: %d, hash: " UINT32_FORMAT_X_0 ")", target_name, (uint)comp_level, decomp, hash);
+#ifdef ASSERT
+    } else {
+      uint name_offset = entry->offset() + entry->name_offset();
+      uint name_size   = entry->name_size(); // Includes '/0'
+      const char* name = _cache->cache_buffer() + name_offset;
+      if (strncmp(target_name, name, name_size) != 0) {
+        assert(false, "SCA: saved nmethod's name '%s' is different from '%s', hash: " UINT32_FORMAT_X_0, name, target_name, hash);
+      }
+#endif
+    }
+
+    DirectiveSet* directives = DirectivesStack::getMatchingDirective(method, nullptr);
+    if (directives->IgnorePrecompiledOption) {
+      LogStreamHandle(Info, scc, compilation) log;
+      if (log.is_enabled()) {
+        log.print("Ignore cached code entry on level %d for ", comp_level);
+        method->print_value_on(&log);
+      }
+      return nullptr;
+    }
+
+    return entry;
+  }
+  return nullptr;
+}
+
+void SCCache::add_C_string(const char* str) {
+  if (is_on_for_write()) {
+    _cache->add_new_C_string(str);
+  }
+}
+
+bool SCCache::allow_const_field(ciConstant& value) {
+  return !is_on() || !StoreCachedCode // Restrict only when we generate cache
+        // Can not trust primitive too   || !is_reference_type(value.basic_type())
+        // May disable this too for now  || is_reference_type(value.basic_type()) && value.as_object()->should_be_constant()
+        ;
+}
+
+bool SCCache::open_cache(const char* cache_path) {
+  if (LoadCachedCode) {
+    log_info(scc)("Trying to load Startup Code Cache '%s'", cache_path);
+    struct stat st;
+    if (os::stat(cache_path, &st) != 0) {
+      log_warning(scc, init)("Specified Startup Code Cache file not found '%s'", cache_path);
+      return false;
+    } else if ((st.st_mode & S_IFMT) != S_IFREG) {
+      log_warning(scc, init)("Specified Startup Code Cache is not file '%s'", cache_path);
+      return false;
+    }
+    int fd = os::open(cache_path, O_RDONLY | O_BINARY, 0);
+    if (fd < 0) {
+      if (errno == ENOENT) {
+        log_warning(scc, init)("Specified Startup Code Cache file not found '%s'", cache_path);
+      } else {
+        log_warning(scc, init)("Failed to open Startup Code Cache file '%s': (%s)", cache_path, os::strerror(errno));
+      }
+      return false;
+    } else {
+      log_info(scc, init)("Opened for read Startup Code Cache '%s'", cache_path);
+    }
+    SCCache* cache = new SCCache(cache_path, fd, (uint)st.st_size);
+    bool failed = cache->failed();
+    if (::close(fd) < 0) {
+      log_warning(scc)("Failed to close for read Startup Code Cache file '%s'", cache_path);
+      failed = true;
+    }
+    if (failed) {
+      delete cache;
+      _cache = nullptr;
+      return false;
+    }
+    _cache = cache;
+  }
+  if (_cache == nullptr && StoreCachedCode) {
+    SCCache* cache = new SCCache(cache_path, -1 /* fd */, 0 /* size */);
+    if (cache->failed()) {
+      delete cache;
+      _cache = nullptr;
+      return false;
+    }
+    _cache = cache;
+  }
+  return true;
+}
+
+class CachedCodeDirectory : public CachedCodeDirectoryInternal {
+public:
+  int _some_number;
+  InstanceKlass* _some_klass;
+  size_t _my_data_length;
+  void* _my_data;
+};
+
+// Skeleton code for including cached code in CDS:
+//
+// [1] Use CachedCodeDirectory to keep track of all of data related to cached code.
+//     E.g., you can build a hashtable to record what methods have been archived.
+//
+// [2] Memory for all data for cached code, including CachedCodeDirectory, should be
+//     allocated using CDSAccess::allocate_from_code_cache().
+//
+// [3] CachedCodeDirectory must be the very first allocation.
+//
+// [4] Two kinds of pointer can be stored:
+//     - A pointer p that points to metadata. CDSAccess::can_generate_cached_code(p) must return true.
+//     - A pointer to a buffer returned by CDSAccess::allocate_from_code_cache().
+//       (It's OK to point to an interior location within this buffer).
+//     Such pointers must be stored using CDSAccess::set_pointer()
+//
+// The buffers allocated by CDSAccess::allocate_from_code_cache() are in a contiguous region. At runtime, this
+// region is mapped to the beginning of the CodeCache (see _cds_code_space in codeCache.cpp). All the pointers
+// in this buffer are relocated as necessary (e.g., to account for the runtime location of the CodeCache).
+//
+// Example:
+//
+// # make sure hw.cds doesn't exist, so that it's regenerated (1.5 step training)
+// $ rm -f hw.cds; java -Xlog:cds,scc::uptime,tags,pid -XX:CacheDataStore=hw.cds -cp ~/tmp/HelloWorld.jar HelloWorld
+//
+// # After training is finish, hw.cds should contain a CachedCodeDirectory. You can see the effect of relocation
+// # from the [scc] log.
+// $ java -Xlog:cds,scc -XX:CacheDataStore=hw.cds -cp ~/tmp/HelloWorld.jar HelloWorld
+// [0.016s][info][scc] new workflow: cached code mapped at 0x7fef97ebc000
+// [0.016s][info][scc] _cached_code_directory->_some_klass     = 0x800009ca8 (java.lang.String)
+// [0.016s][info][scc] _cached_code_directory->_some_number    = 0
+// [0.016s][info][scc] _cached_code_directory->_my_data_length = 0
+// [0.016s][info][scc] _cached_code_directory->_my_data        = 0x7fef97ebc020 (32 bytes offset from base)
+//
+// The 1.5 step training may be hard to debug. If you want to run in a debugger, run the above training step
+// with an additional "-XX:+CDSManualFinalImage" command-line argument.
+
+// This is always at the very beginning of the mmaped CDS "cc" (cached code) region
+static CachedCodeDirectory* _cached_code_directory = nullptr;
+
+#if INCLUDE_CDS_JAVA_HEAP
+void SCCache::new_workflow_start_writing_cache() {
+  CachedCodeDirectory* dir = (CachedCodeDirectory*)CDSAccess::allocate_from_code_cache(sizeof(CachedCodeDirectory));
+  _cached_code_directory = dir;
+
+  CDSAccess::set_pointer(&dir->_some_klass, vmClasses::String_klass());
+
+  size_t n = 120;
+  void* d = (void*)CDSAccess::allocate_from_code_cache(n);
+  CDSAccess::set_pointer(&dir->_my_data, d);
+}
+
+void SCCache::new_workflow_end_writing_cache() {
+  _cached_code_directory->dumptime_init_internal();
+}
+
+void SCCache::new_workflow_load_cache() {
+  void* ptr = CodeCache::map_cached_code();
+  if (ptr != nullptr) {
+    ResourceMark rm;
+    _cached_code_directory = (CachedCodeDirectory*)ptr;
+
+    // CDS uses this to implement CDSAccess::get_archived_object(k)
+    _cached_code_directory->runtime_init_internal();
+
+    // At this point:
+    // - CodeCache::initialize_heaps() has finished.
+    // - CDS archive is fully mapped ("metadata", "heap" and "cached_code" regions are mapped)
+    // - All pointers in the mapped CDS regions are relocated.
+    // - CDSAccess::get_archived_object() works.
+
+    // Data used by AOT compiler
+    InstanceKlass* k = _cached_code_directory->_some_klass;
+    log_info(scc)("new workflow: cached code mapped at %p", ptr);
+    log_info(scc)("_cached_code_directory->_some_klass     = %p (%s)", k, k->external_name());
+    log_info(scc)("_cached_code_directory->_some_number    = %d", _cached_code_directory->_some_number);
+    log_info(scc)("_cached_code_directory->_my_data_length = %zu", _cached_code_directory->_my_data_length);
+    log_info(scc)("_cached_code_directory->_my_data        = %p (%zu bytes offset from base)", _cached_code_directory->_my_data,
+                  pointer_delta((address)_cached_code_directory->_my_data, (address)_cached_code_directory, 1));
+  }
+}
+#endif // INCLUDE_CDS_JAVA_HEAP
+
+#define DATA_ALIGNMENT HeapWordSize
+
+SCCache::SCCache(const char* cache_path, int fd, uint load_size) {
+  _load_header = nullptr;
+  _cache_path = cache_path;
+  _for_read  = LoadCachedCode;
+  _for_write = StoreCachedCode;
+  _load_size = load_size;
+  _store_size = 0;
+  _write_position = 0;
+  _closing  = false;
+  _failed = false;
+  _lookup_failed = false;
+  _table = nullptr;
+  _load_entries = nullptr;
+  _store_entries  = nullptr;
+  _C_strings_buf  = nullptr;
+  _load_buffer = nullptr;
+  _store_buffer = nullptr;
+  _C_load_buffer = nullptr;
+  _C_store_buffer = nullptr;
+  _store_entries_cnt = 0;
+  _gen_preload_code = false;
+  _for_preload = false;       // changed while storing entry data
+  _has_clinit_barriers = false;
+
+  _compile_id = 0;
+  _comp_level = 0;
+
+  _use_meta_ptrs = UseSharedSpaces ? UseMetadataPointers : false;
+
+  // Read header at the begining of cache
+  uint header_size = sizeof(SCCHeader);
+  if (_for_read) {
+    // Read cache
+    _C_load_buffer = NEW_C_HEAP_ARRAY(char, load_size + DATA_ALIGNMENT, mtCode);
+    _load_buffer = align_up(_C_load_buffer, DATA_ALIGNMENT);
+    uint n = (uint)::read(fd, _load_buffer, load_size);
+    if (n != load_size) {
+      log_warning(scc, init)("Failed to read %d bytes at address " INTPTR_FORMAT " from Startup Code Cache file '%s'", load_size, p2i(_load_buffer), _cache_path);
+      set_failed();
+      return;
+    }
+    log_info(scc, init)("Read %d bytes at address " INTPTR_FORMAT " from Startup Code Cache '%s'", load_size, p2i(_load_buffer), _cache_path);
+
+    _load_header = (SCCHeader*)addr(0);
+    const char* scc_jvm_version = addr(_load_header->jvm_version_offset());
+    if (strncmp(scc_jvm_version, VM_Version::internal_vm_info_string(), strlen(scc_jvm_version)) != 0) {
+      log_warning(scc, init)("Disable Startup Code Cache: JVM version '%s' recorded in '%s' does not match current version '%s'", scc_jvm_version, _cache_path, VM_Version::internal_vm_info_string());
+      set_failed();
+      return;
+    }
+    if (!_load_header->verify_config(_cache_path, load_size)) {
+      set_failed();
+      return;
+    }
+    log_info(scc, init)("Read header from Startup Code Cache '%s'", cache_path);
+    if (_load_header->has_meta_ptrs()) {
+      assert(UseSharedSpaces, "should be verified already");
+      _use_meta_ptrs = true; // Regardless UseMetadataPointers
+      UseMetadataPointers = true;
+    }
+    // Read strings
+    load_strings();
+  }
+  if (_for_write) {
+    _gen_preload_code = _use_meta_ptrs && (ClassInitBarrierMode > 0);
+
+    _C_store_buffer = NEW_C_HEAP_ARRAY(char, CachedCodeMaxSize + DATA_ALIGNMENT, mtCode);
+    _store_buffer = align_up(_C_store_buffer, DATA_ALIGNMENT);
+    // Entries allocated at the end of buffer in reverse (as on stack).
+    _store_entries = (SCCEntry*)align_up(_C_store_buffer + CachedCodeMaxSize, DATA_ALIGNMENT);
+    log_info(scc, init)("Allocated store buffer at address " INTPTR_FORMAT " of size %d", p2i(_store_buffer), CachedCodeMaxSize);
+  }
+  _table = new SCAddressTable();
+}
+
+void SCCache::init_table() {
+  SCCache* cache = SCCache::cache();
+  if (cache != nullptr && cache->_table != nullptr) {
+    cache->_table->init();
+  }
+}
+
+void SCCache::init_opto_table() {
+  SCCache* cache = SCCache::cache();
+  if (cache != nullptr && cache->_table != nullptr) {
+    cache->_table->init_opto();
+  }
+}
+
+void SCCache::init_c1_table() {
+  SCCache* cache = SCCache::cache();
+  if (cache != nullptr && cache->_table != nullptr) {
+    cache->_table->init_c1();
+  }
+}
+
+void SCConfig::record(bool use_meta_ptrs) {
+  _flags = 0;
+  if (use_meta_ptrs) {
+    _flags |= metadataPointers;
+  }
+#ifdef ASSERT
+  _flags |= debugVM;
+#endif
+  if (UseCompressedOops) {
+    _flags |= compressedOops;
+  }
+  if (UseCompressedClassPointers) {
+    _flags |= compressedClassPointers;
+  }
+  if (UseTLAB) {
+    _flags |= useTLAB;
+  }
+  if (JavaAssertions::systemClassDefault()) {
+    _flags |= systemClassAssertions;
+  }
+  if (JavaAssertions::userClassDefault()) {
+    _flags |= userClassAssertions;
+  }
+  if (EnableContended) {
+    _flags |= enableContendedPadding;
+  }
+  if (RestrictContended) {
+    _flags |= restrictContendedPadding;
+  }
+  _compressedOopShift    = CompressedOops::shift();
+  _compressedKlassShift  = CompressedKlassPointers::shift();
+  _contendedPaddingWidth = ContendedPaddingWidth;
+  _objectAlignment       = ObjectAlignmentInBytes;
+  _gc                    = (uint)Universe::heap()->kind();
+}
+
+bool SCConfig::verify(const char* cache_path) const {
+#ifdef ASSERT
+  if ((_flags & debugVM) == 0) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created by product VM, it can't be used by debug VM", cache_path);
+    return false;
+  }
+#else
+  if ((_flags & debugVM) != 0) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created by debug VM, it can't be used by product VM", cache_path);
+    return false;
+  }
+#endif
+
+  CollectedHeap::Name scc_gc = (CollectedHeap::Name)_gc;
+  if (scc_gc != Universe::heap()->kind()) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with different GC: %s vs current %s", cache_path, GCConfig::hs_err_name(scc_gc), GCConfig::hs_err_name());
+    return false;
+  }
+
+  if (((_flags & compressedOops) != 0) != UseCompressedOops) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with UseCompressedOops = %s", cache_path, UseCompressedOops ? "false" : "true");
+    return false;
+  }
+  if (((_flags & compressedClassPointers) != 0) != UseCompressedClassPointers) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with UseCompressedClassPointers = %s", cache_path, UseCompressedClassPointers ? "false" : "true");
+    return false;
+  }
+
+  if (((_flags & systemClassAssertions) != 0) != JavaAssertions::systemClassDefault()) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with JavaAssertions::systemClassDefault() = %s", cache_path, JavaAssertions::systemClassDefault() ? "disabled" : "enabled");
+    return false;
+  }
+  if (((_flags & userClassAssertions) != 0) != JavaAssertions::userClassDefault()) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with JavaAssertions::userClassDefault() = %s", cache_path, JavaAssertions::userClassDefault() ? "disabled" : "enabled");
+    return false;
+  }
+
+  if (((_flags & enableContendedPadding) != 0) != EnableContended) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with EnableContended = %s", cache_path, EnableContended ? "false" : "true");
+    return false;
+  }
+  if (((_flags & restrictContendedPadding) != 0) != RestrictContended) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with RestrictContended = %s", cache_path, RestrictContended ? "false" : "true");
+    return false;
+  }
+  if (_compressedOopShift != (uint)CompressedOops::shift()) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with CompressedOops::shift() = %d vs current %d", cache_path, _compressedOopShift, CompressedOops::shift());
+    return false;
+  }
+  if (_compressedKlassShift != (uint)CompressedKlassPointers::shift()) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with CompressedKlassPointers::shift() = %d vs current %d", cache_path, _compressedKlassShift, CompressedKlassPointers::shift());
+    return false;
+  }
+  if (_contendedPaddingWidth != (uint)ContendedPaddingWidth) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with ContendedPaddingWidth = %d vs current %d", cache_path, _contendedPaddingWidth, ContendedPaddingWidth);
+    return false;
+  }
+  if (_objectAlignment != (uint)ObjectAlignmentInBytes) {
+    log_warning(scc, init)("Disable Startup Code Cache: '%s' was created with ObjectAlignmentInBytes = %d vs current %d", cache_path, _objectAlignment, ObjectAlignmentInBytes);
+    return false;
+  }
+  return true;
+}
+
+bool SCCHeader::verify_config(const char* cache_path, uint load_size) const {
+  if (_version != SCC_VERSION) {
+    log_warning(scc, init)("Disable Startup Code Cache: different SCC version %d vs %d recorded in '%s'", SCC_VERSION, _version, cache_path);
+    return false;
+  }
+  if (_cache_size != load_size) {
+    log_warning(scc, init)("Disable Startup Code Cache: different cached code size %d vs %d recorded in '%s'", load_size, _cache_size, cache_path);
+    return false;
+  }
+  if (has_meta_ptrs() && !UseSharedSpaces) {
+    log_warning(scc, init)("Disable Startup Cached Code: '%s' contains metadata pointers but CDS is off", cache_path);
+    return false;
+  }
+  return true;
+}
+
+volatile int SCCache::_nmethod_readers = 0;
+
+SCCache::~SCCache() {
+  if (_closing) {
+    return; // Already closed
+  }
+  // Stop any further access to cache.
+  // Checked on entry to load_nmethod() and store_nmethod().
+  _closing = true;
+  if (_for_read) {
+    // Wait for all load_nmethod() finish.
+    wait_for_no_nmethod_readers();
+  }
+  // Prevent writing code into cache while we are closing it.
+  // This lock held by ciEnv::register_method() which calls store_nmethod().
+  MutexLocker ml(Compile_lock);
+  if (for_write()) { // Finalize cache
+    finish_write();
+  }
+  FREE_C_HEAP_ARRAY(char, _cache_path);
+  if (_C_load_buffer != nullptr) {
+    FREE_C_HEAP_ARRAY(char, _C_load_buffer);
+    _C_load_buffer = nullptr;
+    _load_buffer = nullptr;
+  }
+  if (_C_store_buffer != nullptr) {
+    FREE_C_HEAP_ARRAY(char, _C_store_buffer);
+    _C_store_buffer = nullptr;
+    _store_buffer = nullptr;
+  }
+  if (_table != nullptr) {
+    delete _table;
+    _table = nullptr;
+  }
+}
+
+SCCache* SCCache::open_for_read() {
+  if (SCCache::is_on_for_read()) {
+    return SCCache::cache();
+  }
+  return nullptr;
+}
+
+SCCache* SCCache::open_for_write() {
+  if (SCCache::is_on_for_write()) {
+    SCCache* cache = SCCache::cache();
+    cache->clear_lookup_failed(); // Reset bit
+    return cache;
+  }
+  return nullptr;
+}
+
+void copy_bytes(const char* from, address to, uint size) {
+  assert(size > 0, "sanity");
+  bool by_words = true;
+  if ((size > 2 * HeapWordSize) && (((intptr_t)from | (intptr_t)to) & (HeapWordSize - 1)) == 0) {
+    // Use wordwise copies if possible:
+    Copy::disjoint_words((HeapWord*)from,
+                         (HeapWord*)to,
+                         ((size_t)size + HeapWordSize-1) / HeapWordSize);
+  } else {
+    by_words = false;
+    Copy::conjoint_jbytes(from, to, (size_t)size);
+  }
+  log_trace(scc)("Copied %d bytes as %s from " INTPTR_FORMAT " to " INTPTR_FORMAT, size, (by_words ? "HeapWord" : "bytes"), p2i(from), p2i(to));
+}
+
+void SCCReader::set_read_position(uint pos) {
+  if (pos == _read_position) {
+    return;
+  }
+  assert(pos < _cache->load_size(), "offset:%d >= file size:%d", pos, _cache->load_size());
+  _read_position = pos;
+}
+
+bool SCCache::set_write_position(uint pos) {
+  if (pos == _write_position) {
+    return true;
+  }
+  if (_store_size < _write_position) {
+    _store_size = _write_position; // Adjust during write
+  }
+  assert(pos < _store_size, "offset:%d >= file size:%d", pos, _store_size);
+  _write_position = pos;
+  return true;
+}
+
+static char align_buffer[256] = { 0 };
+
+bool SCCache::align_write() {
+  // We are not executing code from cache - we copy it by bytes first.
+  // No need for big alignment (or at all).
+  uint padding = DATA_ALIGNMENT - (_write_position & (DATA_ALIGNMENT - 1));
+  if (padding == DATA_ALIGNMENT) {
+    return true;
+  }
+  uint n = write_bytes((const void*)&align_buffer, padding);
+  if (n != padding) {
+    return false;
+  }
+  log_trace(scc)("Adjust write alignment in Startup Code Cache '%s'", _cache_path);
+  return true;
+}
+
+uint SCCache::write_bytes(const void* buffer, uint nbytes) {
+  assert(for_write(), "Code Cache file is not created");
+  if (nbytes == 0) {
+    return 0;
+  }
+  uint new_position = _write_position + nbytes;
+  if (new_position >= (uint)((char*)_store_entries - _store_buffer)) {
+    log_warning(scc)("Failed to write %d bytes at offset %d to Startup Code Cache file '%s'. Increase CachedCodeMaxSize.",
+                     nbytes, _write_position, _cache_path);
+    set_failed();
+    exit_vm_on_store_failure();
+    return 0;
+  }
+  copy_bytes((const char* )buffer, (address)(_store_buffer + _write_position), nbytes);
+  log_trace(scc)("Wrote %d bytes at offset %d to Startup Code Cache '%s'", nbytes, _write_position, _cache_path);
+  _write_position += nbytes;
+  if (_store_size < _write_position) {
+    _store_size = _write_position;
+  }
+  return nbytes;
+}
+
+void SCCEntry::update_method_for_writing() {
+  if (_method != nullptr) {
+    _method = CDSAccess::method_in_cached_code(_method);
+  }
+}
+
+void SCCEntry::print(outputStream* st) const {
+  st->print_cr(" SCA entry " INTPTR_FORMAT " [kind: %d, id: " UINT32_FORMAT_X_0 ", offset: %d, size: %d, comp_level: %d, comp_id: %d, decompiled: %d, %s%s%s%s%s]",
+               p2i(this), (int)_kind, _id, _offset, _size, _comp_level, _comp_id, _decompile,
+               (_not_entrant? "not_entrant" : "entrant"),
+               (_loaded ? ", loaded" : ""),
+               (_has_clinit_barriers ? ", has_clinit_barriers" : ""),
+               (_for_preload ? ", for_preload" : ""),
+               (_ignore_decompile ? ", ignore_decomp" : ""));
+}
+
+void* SCCEntry::operator new(size_t x, SCCache* cache) {
+  return (void*)(cache->add_entry());
+}
+
+bool skip_preload(methodHandle mh) {
+  if (!mh->method_holder()->is_loaded()) {
+    return true;
+  }
+  DirectiveSet* directives = DirectivesStack::getMatchingDirective(mh, nullptr);
+  if (directives->DontPreloadOption) {
+    LogStreamHandle(Info, scc, init) log;
+    if (log.is_enabled()) {
+      log.print("Exclude preloading code for ");
+      mh->print_value_on(&log);
+    }
+    return true;
+  }
+  return false;
+}
+
+void SCCache::preload_startup_code(TRAPS) {
+  if (CompilationPolicy::compiler_count(CompLevel_full_optimization) == 0) {
+    // Since we reuse the CompilerBroker API to install cached code, we're required to have a JIT compiler for the
+    // level we want (that is CompLevel_full_optimization).
+    return;
+  }
+  assert(_for_read, "sanity");
+  uint count = _load_header->entries_count();
+  if (_load_entries == nullptr) {
+    // Read it
+    _search_entries = (uint*)addr(_load_header->entries_offset()); // [id, index]
+    _load_entries = (SCCEntry*)(_search_entries + 2 * count);
+    log_info(scc, init)("Read %d entries table at offset %d from Startup Code Cache '%s'", count, _load_header->entries_offset(), _cache_path);
+  }
+  uint preload_entries_count = _load_header->preload_entries_count();
+  if (preload_entries_count > 0) {
+    uint* entries_index = (uint*)addr(_load_header->preload_entries_offset());
+    log_info(scc, init)("Load %d preload entries from Startup Code Cache '%s'", preload_entries_count, _cache_path);
+    uint count = MIN2(preload_entries_count, SCLoadStop);
+    for (uint i = SCLoadStart; i < count; i++) {
+      uint index = entries_index[i];
+      SCCEntry* entry = &(_load_entries[index]);
+      if (entry->not_entrant()) {
+        continue;
+      }
+      methodHandle mh(THREAD, entry->method());
+      assert((mh.not_null() && MetaspaceShared::is_in_shared_metaspace((address)mh())), "sanity");
+      if (skip_preload(mh)) {
+        continue; // Exclude preloading for this method
+      }
+      assert(mh->method_holder()->is_loaded(), "");
+      if (!mh->method_holder()->is_linked()) {
+        assert(!HAS_PENDING_EXCEPTION, "");
+        mh->method_holder()->link_class(THREAD);
+        if (HAS_PENDING_EXCEPTION) {
+          LogStreamHandle(Info, scc) log;
+          if (log.is_enabled()) {
+            ResourceMark rm;
+            log.print("Linkage failed for %s: ", mh->method_holder()->external_name());
+            THREAD->pending_exception()->print_value_on(&log);
+            if (log_is_enabled(Debug, scc)) {
+              THREAD->pending_exception()->print_on(&log);
+            }
+          }
+          CLEAR_PENDING_EXCEPTION;
+        }
+      }
+      if (mh->scc_entry() != nullptr) {
+        // Second C2 compilation of the same method could happen for
+        // different reasons without marking first entry as not entrant.
+        continue; // Keep old entry to avoid issues
+      }
+      mh->set_scc_entry(entry);
+      CompileBroker::compile_method(mh, InvocationEntryBci, CompLevel_full_optimization, methodHandle(), 0, false, CompileTask::Reason_Preload, CHECK);
+    }
+  }
+}
+
+static bool check_entry(SCCEntry::Kind kind, uint id, uint comp_level, uint decomp, SCCEntry* entry) {
+  if (entry->kind() == kind) {
+    assert(entry->id() == id, "sanity");
+    if (kind != SCCEntry::Code || (!entry->not_entrant() && !entry->has_clinit_barriers() &&
+                                  (entry->comp_level() == comp_level) &&
+                                  (entry->ignore_decompile() || entry->decompile() == decomp))) {
+      return true; // Found
+    }
+  }
+  return false;
+}
+
+SCCEntry* SCCache::find_entry(SCCEntry::Kind kind, uint id, uint comp_level, uint decomp) {
+  assert(_for_read, "sanity");
+  uint count = _load_header->entries_count();
+  if (_load_entries == nullptr) {
+    // Read it
+    _search_entries = (uint*)addr(_load_header->entries_offset()); // [id, index]
+    _load_entries = (SCCEntry*)(_search_entries + 2 * count);
+    log_info(scc, init)("Read %d entries table at offset %d from Startup Code Cache '%s'", count, _load_header->entries_offset(), _cache_path);
+  }
+  // Binary search
+  int l = 0;
+  int h = count - 1;
+  while (l <= h) {
+    int mid = (l + h) >> 1;
+    int ix = mid * 2;
+    uint is = _search_entries[ix];
+    if (is == id) {
+      int index = _search_entries[ix + 1];
+      SCCEntry* entry = &(_load_entries[index]);
+      if (check_entry(kind, id, comp_level, decomp, entry)) {
+        return entry; // Found
+      }
+      // Leaner search around (could be the same nmethod with different decompile count)
+      for (int i = mid - 1; i >= l; i--) { // search back
+        ix = i * 2;
+        is = _search_entries[ix];
+        if (is != id) {
+          break;
+        }
+        index = _search_entries[ix + 1];
+        SCCEntry* entry = &(_load_entries[index]);
+        if (check_entry(kind, id, comp_level, decomp, entry)) {
+          return entry; // Found
+        }
+      }
+      for (int i = mid + 1; i <= h; i++) { // search forward
+        ix = i * 2;
+        is = _search_entries[ix];
+        if (is != id) {
+          break;
+        }
+        index = _search_entries[ix + 1];
+        SCCEntry* entry = &(_load_entries[index]);
+        if (check_entry(kind, id, comp_level, decomp, entry)) {
+          return entry; // Found
+        }
+      }
+      break; // Not found match (different decompile count or not_entrant state).
+    } else if (is < id) {
+      l = mid + 1;
+    } else {
+      h = mid - 1;
+    }
+  }
+  return nullptr;
+}
+
+void SCCache::invalidate_entry(SCCEntry* entry) {
+  assert(entry!= nullptr, "all entries should be read already");
+  if (entry->not_entrant()) {
+    return; // Someone invalidated it already
+  }
+#ifdef ASSERT
+  bool found = false;
+  if (_for_read) {
+    uint count = _load_header->entries_count();
+    uint i = 0;
+    for(; i < count; i++) {
+      if (entry == &(_load_entries[i])) {
+        break;
+      }
+    }
+    found = (i < count);
+  }
+  if (!found && _for_write) {
+    uint count = _store_entries_cnt;
+    uint i = 0;
+    for(; i < count; i++) {
+      if (entry == &(_store_entries[i])) {
+        break;
+      }
+    }
+    found = (i < count);
+  }
+  assert(found, "entry should exist");
+#endif
+  entry->set_not_entrant();
+  {
+    uint name_offset = entry->offset() + entry->name_offset();
+    const char* name;
+    if (SCCache::is_loaded(entry)) {
+      name = _load_buffer + name_offset;
+    } else {
+      name = _store_buffer + name_offset;
+    }
+    uint level   = entry->comp_level();
+    uint comp_id = entry->comp_id();
+    uint decomp  = entry->decompile();
+    bool clinit_brs = entry->has_clinit_barriers();
+    log_info(scc, nmethod)("Invalidated entry for '%s' (comp_id %d, comp_level %d, decomp: %d, hash: " UINT32_FORMAT_X_0 "%s)",
+                           name, comp_id, level, decomp, entry->id(), (clinit_brs ? ", has clinit barriers" : ""));
+  }
+  if (entry->next() != nullptr) {
+    entry = entry->next();
+    assert(entry->has_clinit_barriers(), "expecting only such entries here");
+    invalidate_entry(entry);
+  }
+}
+
+extern "C" {
+  static int uint_cmp(const void *i, const void *j) {
+    uint a = *(uint *)i;
+    uint b = *(uint *)j;
+    return a > b ? 1 : a < b ? -1 : 0;
+  }
+}
+
+bool SCCache::finish_write() {
+  if (!align_write()) {
+    return false;
+  }
+  uint strings_offset = _write_position;
+  int strings_count = store_strings();
+  if (strings_count < 0) {
+    return false;
+  }
+  if (!align_write()) {
+    return false;
+  }
+  uint strings_size = _write_position - strings_offset;
+
+  uint entries_count = 0; // Number of entrant (useful) code entries
+  uint entries_offset = _write_position;
+
+  uint store_count = _store_entries_cnt;
+  if (store_count > 0) {
+    uint header_size = (uint)align_up(sizeof(SCCHeader),  DATA_ALIGNMENT);
+    const char* vm_version = VM_Version::internal_vm_info_string();
+    uint vm_version_size = (uint)align_up(strlen(vm_version) + 1, DATA_ALIGNMENT);
+    uint load_count = (_load_header != nullptr) ? _load_header->entries_count() : 0;
+    uint code_count = store_count + load_count;
+    uint search_count = code_count * 2;
+    uint search_size = search_count * sizeof(uint);
+    uint entries_size = (uint)align_up(code_count * sizeof(SCCEntry), DATA_ALIGNMENT); // In bytes
+    uint preload_entries_cnt = 0;
+    uint* preload_entries = NEW_C_HEAP_ARRAY(uint, code_count, mtCode);
+    uint preload_entries_size = code_count * sizeof(uint);
+    // _write_position should include code and strings
+    uint code_alignment = code_count * DATA_ALIGNMENT; // We align_up code size when storing it.
+    uint total_size = _write_position + _load_size + header_size + vm_version_size +
+                     code_alignment + search_size + preload_entries_size + entries_size;
+
+    // Create ordered search table for entries [id, index];
+    uint* search = NEW_C_HEAP_ARRAY(uint, search_count, mtCode);
+    char* buffer = NEW_C_HEAP_ARRAY(char, total_size + DATA_ALIGNMENT, mtCode);
+    char* start = align_up(buffer, DATA_ALIGNMENT);
+    char* current = start + header_size; // Skip header
+    uint jvm_version_offset = current - start;
+    copy_bytes(vm_version, (address)current, (uint)strlen(vm_version) + 1);
+    current += vm_version_size;
+
+    SCCEntry* entries_address = _store_entries; // Pointer to latest entry
+    uint not_entrant_nb = 0;
+    uint max_size = 0;
+    // Add old entries first
+    if (_for_read && (_load_header != nullptr)) {
+      for(uint i = 0; i < load_count; i++) {
+        if (_load_entries[i].load_fail()) {
+          continue;
+        }
+        if (_load_entries[i].not_entrant()) {
+          log_info(scc, exit)("Not entrant load entry id: %d, decomp: %d, hash: " UINT32_FORMAT_X_0, i, _load_entries[i].decompile(), _load_entries[i].id());
+          not_entrant_nb++;
+          if (_load_entries[i].for_preload()) {
+            // Skip not entrant preload code:
+            // we can't pre-load code which may have failing dependencies.
+            continue;
+          }
+          _load_entries[i].set_entrant(); // Reset
+        } else if (_load_entries[i].for_preload() && _load_entries[i].method() != nullptr) {
+          // record entrant first version code for pre-loading
+          preload_entries[preload_entries_cnt++] = entries_count;
+        }
+        {
+          uint size = align_up(_load_entries[i].size(), DATA_ALIGNMENT);
+          if (size > max_size) {
+            max_size = size;
+          }
+          copy_bytes((_load_buffer + _load_entries[i].offset()), (address)current, size);
+          _load_entries[i].set_offset(current - start); // New offset
+          current += size;
+          uint n = write_bytes(&(_load_entries[i]), sizeof(SCCEntry));
+          if (n != sizeof(SCCEntry)) {
+            FREE_C_HEAP_ARRAY(char, buffer);
+            FREE_C_HEAP_ARRAY(uint, search);
+            return false;
+          }
+          search[entries_count*2 + 0] = _load_entries[i].id();
+          search[entries_count*2 + 1] = entries_count;
+          entries_count++;
+        }
+      }
+    }
+    // SCCEntry entries were allocated in reverse in store buffer.
+    // Process them in reverse order to cache first code first.
+    for (int i = store_count - 1; i >= 0; i--) {
+      if (entries_address[i].load_fail()) {
+        continue;
+      }
+      if (entries_address[i].not_entrant()) {
+        log_info(scc, exit)("Not entrant new entry comp_id: %d, comp_level: %d, decomp: %d, hash: " UINT32_FORMAT_X_0 "%s", entries_address[i].comp_id(), entries_address[i].comp_level(), entries_address[i].decompile(), entries_address[i].id(), (entries_address[i].has_clinit_barriers() ? ", has clinit barriers" : ""));
+        not_entrant_nb++;
+        if (entries_address[i].for_preload()) {
+          // Skip not entrant preload code:
+          // we can't pre-load code which may have failing dependencies.
+          continue;
+        }
+        entries_address[i].set_entrant(); // Reset
+      } else if (entries_address[i].for_preload() && entries_address[i].method() != nullptr) {
+        // record entrant first version code for pre-loading
+        preload_entries[preload_entries_cnt++] = entries_count;
+      }
+      {
+        entries_address[i].set_next(nullptr); // clear pointers before storing data
+        uint size = align_up(entries_address[i].size(), DATA_ALIGNMENT);
+        if (size > max_size) {
+          max_size = size;
+        }
+        copy_bytes((_store_buffer + entries_address[i].offset()), (address)current, size);
+        entries_address[i].set_offset(current - start); // New offset
+        entries_address[i].update_method_for_writing();
+        current += size;
+        uint n = write_bytes(&(entries_address[i]), sizeof(SCCEntry));
+        if (n != sizeof(SCCEntry)) {
+          FREE_C_HEAP_ARRAY(char, buffer);
+          FREE_C_HEAP_ARRAY(uint, search);
+          return false;
+        }
+        search[entries_count*2 + 0] = entries_address[i].id();
+        search[entries_count*2 + 1] = entries_count;
+        entries_count++;
+      }
+    }
+    if (entries_count == 0) {
+      log_info(scc, exit)("No new entires, cache files %s was not %s", _cache_path, (_for_read ? "updated" : "created"));
+      FREE_C_HEAP_ARRAY(char, buffer);
+      FREE_C_HEAP_ARRAY(uint, search);
+      return true; // Nothing to write
+    }
+    assert(entries_count <= (store_count + load_count), "%d > (%d + %d)", entries_count, store_count, load_count);
+    // Write strings
+    if (strings_count > 0) {
+      copy_bytes((_store_buffer + strings_offset), (address)current, strings_size);
+      strings_offset = (current - start); // New offset
+      current += strings_size;
+    }
+    uint preload_entries_offset = (current - start);
+    preload_entries_size = preload_entries_cnt * sizeof(uint);
+    if (preload_entries_size > 0) {
+      copy_bytes((const char*)preload_entries, (address)current, preload_entries_size);
+      current += preload_entries_size;
+      log_info(scc, exit)("Wrote %d preload entries to Startup Code Cache '%s'", preload_entries_cnt, _cache_path);
+    }
+    if (preload_entries != nullptr) {
+      FREE_C_HEAP_ARRAY(uint, preload_entries);
+    }
+
+    uint new_entries_offset = (current - start); // New offset
+    // Sort and store search table
+    qsort(search, entries_count, 2*sizeof(uint), uint_cmp);
+    search_size = 2 * entries_count * sizeof(uint);
+    copy_bytes((const char*)search, (address)current, search_size);
+    FREE_C_HEAP_ARRAY(uint, search);
+    current += search_size;
+
+    // Write entries
+    entries_size = entries_count * sizeof(SCCEntry); // New size
+    copy_bytes((_store_buffer + entries_offset), (address)current, entries_size);
+    current += entries_size;
+    log_info(scc, exit)("Wrote %d SCCEntry entries (%d were not entrant, %d max size) to Startup Code Cache '%s'", entries_count, not_entrant_nb, max_size, _cache_path);
+
+    uint size = (current - start);
+    assert(size <= total_size, "%d > %d", size , total_size);
+
+    // Finalize header
+    SCCHeader* header = (SCCHeader*)start;
+    header->init(jvm_version_offset, size,
+                 (uint)strings_count, strings_offset,
+                 entries_count, new_entries_offset,
+                 preload_entries_cnt, preload_entries_offset,
+                 _use_meta_ptrs);
+    log_info(scc, init)("Wrote header to Startup Code Cache '%s'", _cache_path);
+
+    // Now store to file
+#ifdef _WINDOWS  // On Windows, need WRITE permission to remove the file.
+    chmod(_cache_path, _S_IREAD | _S_IWRITE);
+#endif
+    // Use remove() to delete the existing file because, on Unix, this will
+    // allow processes that have it open continued access to the file.
+    remove(_cache_path);
+    int fd = os::open(_cache_path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0444);
+    if (fd < 0) {
+      log_warning(scc, exit)("Unable to create Startup Code Cache file '%s': (%s)", _cache_path, os::strerror(errno));
+      FREE_C_HEAP_ARRAY(char, buffer);
+      exit_vm_on_store_failure();
+      return false;
+    } else {
+      log_info(scc, exit)("Opened for write Startup Code Cache '%s'", _cache_path);
+    }
+    bool success = os::write(fd, start, (size_t)size);
+    if (!success) {
+      log_warning(scc, exit)("Failed to write %d bytes to Startup Code Cache file '%s': (%s)", size, _cache_path, os::strerror(errno));
+      FREE_C_HEAP_ARRAY(char, buffer);
+      exit_vm_on_store_failure();
+      return false;
+    }
+    log_info(scc, exit)("Wrote %d bytes to Startup Code Cache '%s'", size, _cache_path);
+    if (::close(fd) < 0) {
+      log_warning(scc, exit)("Failed to close for write Startup Code Cache file '%s'", _cache_path);
+      exit_vm_on_store_failure();
+    } else {
+      log_info(scc, exit)("Closed for write Startup Code Cache '%s'", _cache_path);
+    }
+    FREE_C_HEAP_ARRAY(char, buffer);
+  }
+  return true;
+}
+
+bool SCCache::load_stub(StubCodeGenerator* cgen, vmIntrinsicID id, const char* name, address start) {
+  assert(start == cgen->assembler()->pc(), "wrong buffer");
+  SCCache* cache = open_for_read();
+  if (cache == nullptr) {
+    return false;
+  }
+  SCCEntry* entry = cache->find_entry(SCCEntry::Stub, (uint)id);
+  if (entry == nullptr) {
+    return false;
+  }
+  uint entry_position = entry->offset();
+  // Read name
+  uint name_offset = entry->name_offset() + entry_position;
+  uint name_size   = entry->name_size(); // Includes '/0'
+  const char* saved_name = cache->addr(name_offset);
+  if (strncmp(name, saved_name, (name_size - 1)) != 0) {
+    log_warning(scc)("Saved stub's name '%s' is different from '%s' for id:%d", saved_name, name, (int)id);
+    cache->set_failed();
+    exit_vm_on_load_failure();
+    return false;
+  }
+  log_info(scc,stubs)("Reading stub '%s' id:%d from Startup Code Cache '%s'", name, (int)id, cache->_cache_path);
+  // Read code
+  uint code_offset = entry->code_offset() + entry_position;
+  uint code_size   = entry->code_size();
+  copy_bytes(cache->addr(code_offset), start, code_size);
+  cgen->assembler()->code_section()->set_end(start + code_size);
+  log_info(scc,stubs)("Read stub '%s' id:%d from Startup Code Cache '%s'", name, (int)id, cache->_cache_path);
+  return true;
+}
+
+bool SCCache::store_stub(StubCodeGenerator* cgen, vmIntrinsicID id, const char* name, address start) {
+  SCCache* cache = open_for_write();
+  if (cache == nullptr) {
+    return false;
+  }
+  log_info(scc, stubs)("Writing stub '%s' id:%d to Startup Code Cache '%s'", name, (int)id, cache->_cache_path);
+  if (!cache->align_write()) {
+    return false;
+  }
+#ifdef ASSERT
+  CodeSection* cs = cgen->assembler()->code_section();
+  if (cs->has_locs()) {
+    uint reloc_count = cs->locs_count();
+    tty->print_cr("======== write stubs code section relocations [%d]:", reloc_count);
+    // Collect additional data
+    RelocIterator iter(cs);
+    while (iter.next()) {
+      switch (iter.type()) {
+        case relocInfo::none:
+          break;
+        default: {
+          iter.print_current_on(tty);
+          fatal("stub's relocation %d unimplemented", (int)iter.type());
+          break;
+        }
+      }
+    }
+  }
+#endif
+  uint entry_position = cache->_write_position;
+
+  // Write code
+  uint code_offset = 0;
+  uint code_size = cgen->assembler()->pc() - start;
+  uint n = cache->write_bytes(start, code_size);
+  if (n != code_size) {
+    return false;
+  }
+  // Write name
+  uint name_offset = cache->_write_position - entry_position;
+  uint name_size = (uint)strlen(name) + 1; // Includes '/0'
+  n = cache->write_bytes(name, name_size);
+  if (n != name_size) {
+    return false;
+  }
+  uint entry_size = cache->_write_position - entry_position;
+  SCCEntry* entry = new(cache) SCCEntry(entry_position, entry_size, name_offset, name_size,
+                                          code_offset, code_size, 0, 0,
+                                          SCCEntry::Stub, (uint32_t)id);
+  log_info(scc, stubs)("Wrote stub '%s' id:%d to Startup Code Cache '%s'", name, (int)id, cache->_cache_path);
+  return true;
+}
+
+Klass* SCCReader::read_klass(const methodHandle& comp_method, bool shared) {
+  uint code_offset = read_position();
+  uint state = *(uint*)addr(code_offset);
+  uint init_state = (state  & 1);
+  uint array_dim  = (state >> 1);
+  code_offset += sizeof(int);
+  if (_cache->use_meta_ptrs() && shared) {
+    uint klass_offset = *(uint*)addr(code_offset);
+    code_offset += sizeof(uint);
+    set_read_position(code_offset);
+    Klass* k = (Klass*)((address)SharedBaseAddress + klass_offset);
+    if (!MetaspaceShared::is_in_shared_metaspace((address)k)) {
+      // Something changed in CDS
+      set_lookup_failed();
+      log_info(scc)("Lookup failed for shared klass: " INTPTR_FORMAT " is not in CDS ", p2i((address)k));
+      return nullptr;
+    }
+    assert(k->is_klass(), "sanity");
+    ResourceMark rm;
+    const char* comp_name = comp_method->name_and_sig_as_C_string();
+    if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_loaded()) {
+      set_lookup_failed();
+      log_info(scc)("%d '%s' (L%d): Lookup failed for klass %s: not loaded",
+                       compile_id(), comp_name, comp_level(), k->external_name());
+      return nullptr;
+    } else
+    // Allow not initialized klass which was uninitialized during code caching or for preload
+    if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_initialized() && (init_state == 1) && !_preload) {
+      set_lookup_failed();
+      log_info(scc)("%d '%s' (L%d): Lookup failed for klass %s: not initialized",
+                       compile_id(), comp_name, comp_level(), k->external_name());
+      return nullptr;
+    }
+    if (array_dim > 0) {
+      assert(k->is_instance_klass() || k->is_typeArray_klass(), "sanity check");
+      Klass* ak = k->array_klass_or_null(array_dim);
+      // FIXME: what would it take to create an array class on the fly?
+//      Klass* ak = k->array_klass(dim, JavaThread::current());
+//      guarantee(JavaThread::current()->pending_exception() == nullptr, "");
+      if (ak == nullptr) {
+        set_lookup_failed();
+        log_info(scc)("%d (L%d): %d-dimension array klass lookup failed: %s",
+                         compile_id(), comp_level(), array_dim, k->external_name());
+      }
+      log_info(scc)("%d (L%d): Klass lookup: %s (object array)", compile_id(), comp_level(), k->external_name());
+      return ak;
+    } else {
+      log_info(scc)("%d (L%d): Shared klass lookup: %s",
+                    compile_id(), comp_level(), k->external_name());
+      return k;
+    }
+  }
+  int name_length = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  const char* dest = addr(code_offset);
+  code_offset += name_length + 1;
+  set_read_position(code_offset);
+  TempNewSymbol klass_sym = SymbolTable::probe(&(dest[0]), name_length);
+  if (klass_sym == nullptr) {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Probe failed for class %s",
+                     compile_id(), comp_level(), &(dest[0]));
+    return nullptr;
+  }
+  // Use class loader of compiled method.
+  Thread* thread = Thread::current();
+  Handle loader(thread, comp_method->method_holder()->class_loader());
+  Handle protection_domain(thread, comp_method->method_holder()->protection_domain());
+  Klass* k = SystemDictionary::find_instance_or_array_klass(thread, klass_sym, loader, protection_domain);
+  assert(!thread->has_pending_exception(), "should not throw");
+  if (k == nullptr && !loader.is_null()) {
+    // Try default loader and domain
+    k = SystemDictionary::find_instance_or_array_klass(thread, klass_sym, Handle(), Handle());
+    assert(!thread->has_pending_exception(), "should not throw");
+  }
+  if (k != nullptr) {
+    // Allow not initialized klass which was uninitialized during code caching
+    if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_initialized() && (init_state == 1)) {
+      set_lookup_failed();
+      log_info(scc)("%d (L%d): Lookup failed for klass %s: not initialized", compile_id(), comp_level(), &(dest[0]));
+      return nullptr;
+    }
+    log_info(scc)("%d (L%d): Klass lookup %s", compile_id(), comp_level(), k->external_name());
+  } else {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Lookup failed for class %s", compile_id(), comp_level(), &(dest[0]));
+    return nullptr;
+  }
+  return k;
+}
+
+Method* SCCReader::read_method(const methodHandle& comp_method, bool shared) {
+  uint code_offset = read_position();
+  if (_cache->use_meta_ptrs() && shared) {
+    uint method_offset = *(uint*)addr(code_offset);
+    code_offset += sizeof(uint);
+    set_read_position(code_offset);
+    Method* m = (Method*)((address)SharedBaseAddress + method_offset);
+    if (!MetaspaceShared::is_in_shared_metaspace((address)m)) {
+      // Something changed in CDS
+      set_lookup_failed();
+      log_info(scc)("Lookup failed for shared method: " INTPTR_FORMAT " is not in CDS ", p2i((address)m));
+      return nullptr;
+    }
+    assert(m->is_method(), "sanity");
+    ResourceMark rm;
+    const char* comp_name = comp_method->name_and_sig_as_C_string();
+    Klass* k = m->method_holder();
+    if (!k->is_instance_klass()) {
+      set_lookup_failed();
+      log_info(scc)("%d '%s' (L%d): Lookup failed for holder %s: not instance klass", compile_id(), comp_name, comp_level(), k->external_name());
+      return nullptr;
+    } else if (!MetaspaceShared::is_in_shared_metaspace((address)k)) {
+      set_lookup_failed();
+      log_info(scc)("%d '%s' (L%d): Lookup failed for holder %s: not in CDS", compile_id(), comp_name, comp_level(), k->external_name());
+      return nullptr;
+    } else if (!InstanceKlass::cast(k)->is_loaded()) {
+      set_lookup_failed();
+      log_info(scc)("%d '%s' (L%d): Lookup failed for holder %s: not loaded", compile_id(), comp_name, comp_level(), k->external_name());
+      return nullptr;
+    } else if (!InstanceKlass::cast(k)->is_linked()) {
+      set_lookup_failed();
+      log_info(scc)("%d '%s' (L%d): Lookup failed for holder %s: not linked%s", compile_id(), comp_name, comp_level(), k->external_name(), (_preload ? " for code preload" : ""));
+      return nullptr;
+    }
+    log_info(scc)("%d (L%d): Shared method lookup: %s", compile_id(), comp_level(), m->name_and_sig_as_C_string());
+    return m;
+  }
+  int holder_length = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  int name_length = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  int signat_length = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+
+  const char* dest = addr(code_offset);
+  code_offset += holder_length + 1 + name_length + 1 + signat_length + 1;
+  set_read_position(code_offset);
+  TempNewSymbol klass_sym = SymbolTable::probe(&(dest[0]), holder_length);
+  if (klass_sym == nullptr) {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Probe failed for class %s", compile_id(), comp_level(), &(dest[0]));
+    return nullptr;
+  }
+  // Use class loader of compiled method.
+  Thread* thread = Thread::current();
+  Handle loader(thread, comp_method->method_holder()->class_loader());
+  Handle protection_domain(thread, comp_method->method_holder()->protection_domain());
+  Klass* k = SystemDictionary::find_instance_or_array_klass(thread, klass_sym, loader, protection_domain);
+  assert(!thread->has_pending_exception(), "should not throw");
+  if (k == nullptr && !loader.is_null()) {
+    // Try default loader and domain
+    k = SystemDictionary::find_instance_or_array_klass(thread, klass_sym, Handle(), Handle());
+    assert(!thread->has_pending_exception(), "should not throw");
+  }
+  if (k != nullptr) {
+    if (!k->is_instance_klass()) {
+      set_lookup_failed();
+      log_info(scc)("%d (L%d): Lookup failed for holder %s: not instance klass",
+                       compile_id(), comp_level(), &(dest[0]));
+      return nullptr;
+    } else if (!InstanceKlass::cast(k)->is_linked()) {
+      set_lookup_failed();
+      log_info(scc)("%d (L%d): Lookup failed for holder %s: not linked",
+                       compile_id(), comp_level(), &(dest[0]));
+      return nullptr;
+    }
+    log_info(scc)("%d (L%d): Holder lookup: %s", compile_id(), comp_level(), k->external_name());
+  } else {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Lookup failed for holder %s",
+                  compile_id(), comp_level(), &(dest[0]));
+    return nullptr;
+  }
+  TempNewSymbol name_sym = SymbolTable::probe(&(dest[holder_length + 1]), name_length);
+  int pos = holder_length + 1 + name_length + 1;
+  TempNewSymbol sign_sym = SymbolTable::probe(&(dest[pos]), signat_length);
+  if (name_sym == nullptr) {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Probe failed for method name %s",
+                     compile_id(), comp_level(), &(dest[holder_length + 1]));
+    return nullptr;
+  }
+  if (sign_sym == nullptr) {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Probe failed for method signature %s",
+                     compile_id(), comp_level(), &(dest[pos]));
+    return nullptr;
+  }
+  Method* m = InstanceKlass::cast(k)->find_method(name_sym, sign_sym);
+  if (m != nullptr) {
+    ResourceMark rm;
+    log_info(scc)("%d (L%d): Method lookup: %s", compile_id(), comp_level(), m->name_and_sig_as_C_string());
+  } else {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Lookup failed for method %s::%s%s",
+                     compile_id(), comp_level(), &(dest[0]), &(dest[holder_length + 1]), &(dest[pos]));
+    return nullptr;
+  }
+  return m;
+}
+
+bool SCCache::write_klass(Klass* klass) {
+  if (klass->is_hidden()) { // Skip such nmethod
+    set_lookup_failed();
+    return false;
+  }
+  bool can_use_meta_ptrs = _use_meta_ptrs;
+  uint array_dim = 0;
+  if (klass->is_objArray_klass()) {
+    array_dim = ObjArrayKlass::cast(klass)->dimension();
+    klass     = ObjArrayKlass::cast(klass)->bottom_klass(); // overwrites klass
+  }
+  uint init_state = 0;
+  if (klass->is_instance_klass()) {
+    InstanceKlass* ik = InstanceKlass::cast(klass);
+    ClassLoaderData* cld = ik->class_loader_data();
+    if (!cld->is_builtin_class_loader_data()) {
+      set_lookup_failed();
+      return false;
+    }
+    if (_for_preload && !CDSAccess::can_generate_cached_code(ik)) {
+      _for_preload = false;
+      // Bailout if code has clinit barriers:
+      // method will be recompiled without them in any case
+      if (_has_clinit_barriers) {
+        set_lookup_failed();
+        return false;
+      }
+      can_use_meta_ptrs = false;
+    }
+    init_state = (ik->is_initialized() ? 1 : 0);
+  }
+  ResourceMark rm;
+  uint state = (array_dim << 1) | (init_state & 1);
+  if (can_use_meta_ptrs && CDSAccess::can_generate_cached_code(klass)) {
+    DataKind kind = DataKind::Klass_Shared;
+    uint n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+    // Record state of instance klass initialization.
+    n = write_bytes(&state, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+    uint klass_offset = CDSAccess::delta_from_shared_address_base((address)klass);
+    n = write_bytes(&klass_offset, sizeof(uint));
+    if (n != sizeof(uint)) {
+      return false;
+    }
+    log_info(scc)("%d (L%d): Wrote shared klass: %s%s%s @ 0x%08x", compile_id(), comp_level(), klass->external_name(),
+                  (!klass->is_instance_klass() ? "" : (init_state == 1 ? " (initialized)" : " (not-initialized)")),
+                  (array_dim > 0 ? " (object array)" : ""),
+                  klass_offset);
+    return true;
+  }
+  // Bailout if code has clinit barriers:
+  // method will be recompiled without them in any case
+  if (_for_preload && _has_clinit_barriers) {
+    set_lookup_failed();
+    return false;
+  }
+  _for_preload = false;
+  log_info(scc,cds)("%d (L%d): Not shared klass: %s", compile_id(), comp_level(), klass->external_name());
+  DataKind kind = DataKind::Klass;
+  uint n = write_bytes(&kind, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  // Record state of instance klass initialization.
+  n = write_bytes(&state, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  Symbol* name = klass->name();
+  int name_length = name->utf8_length();
+  int total_length = name_length + 1;
+  char* dest = NEW_RESOURCE_ARRAY(char, total_length);
+  name->as_C_string(dest, total_length);
+  dest[total_length - 1] = '\0';
+  LogTarget(Info, scc, loader) log;
+  if (log.is_enabled()) {
+    LogStream ls(log);
+    oop loader = klass->class_loader();
+    oop domain = klass->protection_domain();
+    ls.print("Class %s loader: ", dest);
+    if (loader == nullptr) {
+      ls.print("nullptr");
+    } else {
+      loader->print_value_on(&ls);
+    }
+    ls.print(" domain: ");
+    if (domain == nullptr) {
+      ls.print("nullptr");
+    } else {
+      domain->print_value_on(&ls);
+    }
+    ls.cr();
+  }
+  n = write_bytes(&name_length, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  n = write_bytes(dest, total_length);
+  if (n != (uint)total_length) {
+    return false;
+  }
+  log_info(scc)("%d (L%d): Wrote klass: %s%s%s",
+                compile_id(), comp_level(),
+                dest, (!klass->is_instance_klass() ? "" : (init_state == 1 ? " (initialized)" : " (not-initialized)")),
+                (array_dim > 0 ? " (object array)" : ""));
+  return true;
+}
+
+bool SCCache::write_method(Method* method) {
+  bool can_use_meta_ptrs = _use_meta_ptrs;
+  Klass* klass = method->method_holder();
+  if (klass->is_instance_klass()) {
+    InstanceKlass* ik = InstanceKlass::cast(klass);
+    ClassLoaderData* cld = ik->class_loader_data();
+    if (!cld->is_builtin_class_loader_data()) {
+      set_lookup_failed();
+      return false;
+    }
+    if (_for_preload && !CDSAccess::can_generate_cached_code(ik)) {
+      _for_preload = false;
+      // Bailout if code has clinit barriers:
+      // method will be recompiled without them in any case
+      if (_has_clinit_barriers) {
+        set_lookup_failed();
+        return false;
+      }
+      can_use_meta_ptrs = false;
+    }
+  }
+  ResourceMark rm;
+  if (can_use_meta_ptrs && CDSAccess::can_generate_cached_code(method)) {
+    DataKind kind = DataKind::Method_Shared;
+    uint n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+    uint method_offset = CDSAccess::delta_from_shared_address_base((address)method);
+    n = write_bytes(&method_offset, sizeof(uint));
+    if (n != sizeof(uint)) {
+      return false;
+    }
+    log_info(scc)("%d (L%d): Wrote shared method: %s @ 0x%08x", compile_id(), comp_level(), method->name_and_sig_as_C_string(), method_offset);
+    return true;
+  }
+  // Bailout if code has clinit barriers:
+  // method will be recompiled without them in any case
+  if (_for_preload && _has_clinit_barriers) {
+    set_lookup_failed();
+    return false;
+  }
+  _for_preload = false;
+  log_info(scc,cds)("%d (L%d): Not shared method: %s", compile_id(), comp_level(), method->name_and_sig_as_C_string());
+  if (method->is_hidden()) { // Skip such nmethod
+    set_lookup_failed();
+    return false;
+  }
+  DataKind kind = DataKind::Method;
+  uint n = write_bytes(&kind, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  Symbol* name   = method->name();
+  Symbol* holder = method->klass_name();
+  Symbol* signat = method->signature();
+  int name_length   = name->utf8_length();
+  int holder_length = holder->utf8_length();
+  int signat_length = signat->utf8_length();
+
+  // Write sizes and strings
+  int total_length = holder_length + 1 + name_length + 1 + signat_length + 1;
+  char* dest = NEW_RESOURCE_ARRAY(char, total_length);
+  holder->as_C_string(dest, total_length);
+  dest[holder_length] = '\0';
+  int pos = holder_length + 1;
+  name->as_C_string(&(dest[pos]), (total_length - pos));
+  pos += name_length;
+  dest[pos++] = '\0';
+  signat->as_C_string(&(dest[pos]), (total_length - pos));
+  dest[total_length - 1] = '\0';
+
+  LogTarget(Info, scc, loader) log;
+  if (log.is_enabled()) {
+    LogStream ls(log);
+    oop loader = klass->class_loader();
+    oop domain = klass->protection_domain();
+    ls.print("Holder %s loader: ", dest);
+    if (loader == nullptr) {
+      ls.print("nullptr");
+    } else {
+      loader->print_value_on(&ls);
+    }
+    ls.print(" domain: ");
+    if (domain == nullptr) {
+      ls.print("nullptr");
+    } else {
+      domain->print_value_on(&ls);
+    }
+    ls.cr();
+  }
+
+  n = write_bytes(&holder_length, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  n = write_bytes(&name_length, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  n = write_bytes(&signat_length, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  n = write_bytes(dest, total_length);
+  if (n != (uint)total_length) {
+    return false;
+  }
+  dest[holder_length] = ' ';
+  dest[holder_length + 1 + name_length] = ' ';
+  log_info(scc)("%d (L%d): Wrote method: %s", compile_id(), comp_level(), dest);
+  return true;
+}
+
+// Repair the pc relative information in the code after load
+bool SCCReader::read_relocations(CodeBuffer* buffer, CodeBuffer* orig_buffer,
+                                 OopRecorder* oop_recorder, ciMethod* target) {
+  bool success = true;
+  for (int i = 0; i < (int)CodeBuffer::SECT_LIMIT; i++) {
+    uint code_offset = read_position();
+    int reloc_count = *(int*)addr(code_offset);
+    code_offset += sizeof(int);
+    if (reloc_count == 0) {
+      set_read_position(code_offset);
+      continue;
+    }
+    // Read _locs_point (as offset from start)
+    int locs_point_off = *(int*)addr(code_offset);
+    code_offset += sizeof(int);
+    uint reloc_size = reloc_count * sizeof(relocInfo);
+    CodeSection* cs  = buffer->code_section(i);
+    if (cs->locs_capacity() < reloc_count) {
+      cs->expand_locs(reloc_count);
+    }
+    relocInfo* reloc_start = cs->locs_start();
+    copy_bytes(addr(code_offset), (address)reloc_start, reloc_size);
+    code_offset += reloc_size;
+    cs->set_locs_end(reloc_start + reloc_count);
+    cs->set_locs_point(cs->start() + locs_point_off);
+
+    // Read additional relocation data: uint per relocation
+    uint  data_size  = reloc_count * sizeof(uint);
+    uint* reloc_data = (uint*)addr(code_offset);
+    code_offset += data_size;
+    set_read_position(code_offset);
+    LogStreamHandle(Info, scc, reloc) log;
+    if (log.is_enabled()) {
+      log.print_cr("======== read code section %d relocations [%d]:", i, reloc_count);
+    }
+    RelocIterator iter(cs);
+    int j = 0;
+    while (iter.next()) {
+      switch (iter.type()) {
+        case relocInfo::none:
+          break;
+        case relocInfo::oop_type: {
+          VM_ENTRY_MARK;
+          oop_Relocation* r = (oop_Relocation*)iter.reloc();
+          if (r->oop_is_immediate()) {
+            assert(reloc_data[j] == (uint)j, "should be");
+            methodHandle comp_method(THREAD, target->get_Method());
+            jobject jo = read_oop(THREAD, comp_method);
+            if (lookup_failed()) {
+              success = false;
+              break;
+            }
+            r->set_value((address)jo);
+          } else if (false) {
+            // Get already updated value from OopRecorder.
+            assert(oop_recorder != nullptr, "sanity");
+            int index = r->oop_index();
+            jobject jo = oop_recorder->oop_at(index);
+            oop obj = JNIHandles::resolve(jo);
+            r->set_value(*reinterpret_cast<address*>(&obj));
+          }
+          break;
+        }
+        case relocInfo::metadata_type: {
+          VM_ENTRY_MARK;
+          metadata_Relocation* r = (metadata_Relocation*)iter.reloc();
+          Metadata* m;
+          if (r->metadata_is_immediate()) {
+            assert(reloc_data[j] == (uint)j, "should be");
+            methodHandle comp_method(THREAD, target->get_Method());
+            m = read_metadata(comp_method);
+            if (lookup_failed()) {
+              success = false;
+              break;
+            }
+          } else {
+            // Get already updated value from OopRecorder.
+            assert(oop_recorder != nullptr, "sanity");
+            int index = r->metadata_index();
+            m = oop_recorder->metadata_at(index);
+          }
+          r->set_value((address)m);
+          break;
+        }
+        case relocInfo::virtual_call_type:   // Fall through. They all call resolve_*_call blobs.
+        case relocInfo::opt_virtual_call_type:
+        case relocInfo::static_call_type: {
+          address dest = _cache->address_for_id(reloc_data[j]);
+          if (dest != (address)-1) {
+            ((CallRelocation*)iter.reloc())->set_destination(dest);
+          }
+          break;
+        }
+        case relocInfo::trampoline_stub_type: {
+          address dest = _cache->address_for_id(reloc_data[j]);
+          if (dest != (address)-1) {
+            ((trampoline_stub_Relocation*)iter.reloc())->set_destination(dest);
+          }
+          break;
+        }
+        case relocInfo::static_stub_type:
+          break;
+        case relocInfo::runtime_call_type: {
+          address dest = _cache->address_for_id(reloc_data[j]);
+          if (dest != (address)-1) {
+            ((CallRelocation*)iter.reloc())->set_destination(dest);
+          }
+          break;
+        }
+        case relocInfo::runtime_call_w_cp_type:
+          fatal("runtime_call_w_cp_type unimplemented");
+          //address destination = iter.reloc()->value();
+          break;
+        case relocInfo::external_word_type: {
+          address target = _cache->address_for_id(reloc_data[j]);
+          // Add external address to global table
+          int index = ExternalsRecorder::find_index(target);
+          // Update index in relocation
+          Relocation::add_jint(iter.data(), index);
+          external_word_Relocation* reloc = (external_word_Relocation*)iter.reloc();
+          assert(reloc->target() == target, "sanity");
+          reloc->set_value(target); // Patch address in the code
+          iter.reloc()->fix_relocation_after_move(orig_buffer, buffer);
+          break;
+        }
+        case relocInfo::internal_word_type:
+          iter.reloc()->fix_relocation_after_move(orig_buffer, buffer);
+          break;
+        case relocInfo::section_word_type:
+          iter.reloc()->fix_relocation_after_move(orig_buffer, buffer);
+          break;
+        case relocInfo::poll_type:
+          break;
+        case relocInfo::poll_return_type:
+          break;
+        case relocInfo::post_call_nop_type:
+          break;
+        case relocInfo::entry_guard_type:
+          break;
+        default:
+          fatal("relocation %d unimplemented", (int)iter.type());
+          break;
+      }
+      if (success && log.is_enabled()) {
+        iter.print_current_on(&log);
+      }
+      j++;
+    }
+    assert(j <= (int)reloc_count, "sanity");
+  }
+  return success;
+}
+
+bool SCCReader::read_code(CodeBuffer* buffer, CodeBuffer* orig_buffer, uint code_offset) {
+  assert(code_offset == align_up(code_offset, DATA_ALIGNMENT), "%d not aligned to %d", code_offset, DATA_ALIGNMENT);
+  assert(buffer->blob() != nullptr, "sanity");
+  SCCodeSection* scc_cs = (SCCodeSection*)addr(code_offset);
+  for (int i = 0; i < (int)CodeBuffer::SECT_LIMIT; i++) {
+    CodeSection* cs = buffer->code_section(i);
+    // Read original section size and address.
+    uint orig_size = scc_cs[i]._size;
+    log_debug(scc)("======== read code section %d [%d]:", i, orig_size);
+    uint orig_size_align = align_up(orig_size, DATA_ALIGNMENT);
+    if (i != (int)CodeBuffer::SECT_INSTS) {
+      buffer->initialize_section_size(cs, orig_size_align);
+    }
+    if (orig_size_align > (uint)cs->capacity()) { // Will not fit
+      log_info(scc)("%d (L%d): original code section %d size %d > current capacity %d",
+                       compile_id(), comp_level(), i, orig_size, cs->capacity());
+      return false;
+    }
+    if (orig_size == 0) {
+      assert(cs->size() == 0, "should match");
+      continue;  // skip trivial section
+    }
+    address orig_start = scc_cs[i]._origin_address;
+
+    // Populate fake original buffer (no code allocation in CodeCache).
+    // It is used for relocations to calculate sections addesses delta.
+    CodeSection* orig_cs = orig_buffer->code_section(i);
+    assert(!orig_cs->is_allocated(), "This %d section should not be set", i);
+    orig_cs->initialize(orig_start, orig_size);
+
+    // Load code to new buffer.
+    address code_start = cs->start();
+    copy_bytes(addr(scc_cs[i]._offset + code_offset), code_start, orig_size_align);
+    cs->set_end(code_start + orig_size);
+  }
+
+  return true;
+}
+
+bool SCCache::load_exception_blob(CodeBuffer* buffer, int* pc_offset) {
+#ifdef ASSERT
+  LogStreamHandle(Debug, scc, nmethod) log;
+  if (log.is_enabled()) {
+    FlagSetting fs(PrintRelocations, true);
+    buffer->print_on(&log);
+  }
+#endif
+  SCCache* cache = open_for_read();
+  if (cache == nullptr) {
+    return false;
+  }
+  SCCEntry* entry = cache->find_entry(SCCEntry::Blob, 999);
+  if (entry == nullptr) {
+    return false;
+  }
+  SCCReader reader(cache, entry, nullptr);
+  return reader.compile_blob(buffer, pc_offset);
+}
+
+bool SCCReader::compile_blob(CodeBuffer* buffer, int* pc_offset) {
+  uint entry_position = _entry->offset();
+
+  // Read pc_offset
+  *pc_offset = *(int*)addr(entry_position);
+
+  // Read name
+  uint name_offset = entry_position + _entry->name_offset();
+  uint name_size = _entry->name_size(); // Includes '/0'
+  const char* name = addr(name_offset);
+
+  log_info(scc, stubs)("%d (L%d): Reading blob '%s' with pc_offset %d from Startup Code Cache '%s'",
+                       compile_id(), comp_level(), name, *pc_offset, _cache->cache_path());
+
+  if (strncmp(buffer->name(), name, (name_size - 1)) != 0) {
+    log_warning(scc)("%d (L%d): Saved blob's name '%s' is different from '%s'",
+                     compile_id(), comp_level(), name, buffer->name());
+    ((SCCache*)_cache)->set_failed();
+    exit_vm_on_load_failure();
+    return false;
+  }
+
+  // Create fake original CodeBuffer
+  CodeBuffer orig_buffer(name);
+
+  // Read code
+  uint code_offset = entry_position + _entry->code_offset();
+  if (!read_code(buffer, &orig_buffer, code_offset)) {
+    return false;
+  }
+
+  // Read relocations
+  uint reloc_offset = entry_position + _entry->reloc_offset();
+  set_read_position(reloc_offset);
+  if (!read_relocations(buffer, &orig_buffer, nullptr, nullptr)) {
+    return false;
+  }
+
+  log_info(scc, stubs)("%d (L%d): Read blob '%s' from Startup Code Cache '%s'",
+                       compile_id(), comp_level(), name, _cache->cache_path());
+#ifdef ASSERT
+  LogStreamHandle(Debug, scc, nmethod) log;
+  if (log.is_enabled()) {
+    FlagSetting fs(PrintRelocations, true);
+    buffer->print_on(&log);
+    buffer->decode();
+  }
+#endif
+  return true;
+}
+
+bool SCCache::write_relocations(CodeBuffer* buffer, uint& all_reloc_size) {
+  uint all_reloc_count = 0;
+  for (int i = 0; i < (int)CodeBuffer::SECT_LIMIT; i++) {
+    CodeSection* cs = buffer->code_section(i);
+    uint reloc_count = cs->has_locs() ? cs->locs_count() : 0;
+    all_reloc_count += reloc_count;
+  }
+  all_reloc_size = all_reloc_count * sizeof(relocInfo);
+  bool success = true;
+  uint* reloc_data = NEW_C_HEAP_ARRAY(uint, all_reloc_count, mtCode);
+  for (int i = 0; i < (int)CodeBuffer::SECT_LIMIT; i++) {
+    CodeSection* cs = buffer->code_section(i);
+    int reloc_count = cs->has_locs() ? cs->locs_count() : 0;
+    uint n = write_bytes(&reloc_count, sizeof(int));
+    if (n != sizeof(int)) {
+      success = false;
+      break;
+    }
+    if (reloc_count == 0) {
+      continue;
+    }
+    // Write _locs_point (as offset from start)
+    int locs_point_off = cs->locs_point_off();
+    n = write_bytes(&locs_point_off, sizeof(int));
+    if (n != sizeof(int)) {
+      success = false;
+      break;
+    }
+    relocInfo* reloc_start = cs->locs_start();
+    uint reloc_size      = reloc_count * sizeof(relocInfo);
+    n = write_bytes(reloc_start, reloc_size);
+    if (n != reloc_size) {
+      success = false;
+      break;
+    }
+    LogStreamHandle(Info, scc, reloc) log;
+    if (log.is_enabled()) {
+      log.print_cr("======== write code section %d relocations [%d]:", i, reloc_count);
+    }
+    // Collect additional data
+    RelocIterator iter(cs);
+    bool has_immediate = false;
+    int j = 0;
+    while (iter.next()) {
+      reloc_data[j] = 0; // initialize
+      switch (iter.type()) {
+        case relocInfo::none:
+          break;
+        case relocInfo::oop_type: {
+          oop_Relocation* r = (oop_Relocation*)iter.reloc();
+          if (r->oop_is_immediate()) {
+            reloc_data[j] = (uint)j; // Indication that we need to restore immediate
+            has_immediate = true;
+          }
+          break;
+        }
+        case relocInfo::metadata_type: {
+          metadata_Relocation* r = (metadata_Relocation*)iter.reloc();
+          if (r->metadata_is_immediate()) {
+            reloc_data[j] = (uint)j; // Indication that we need to restore immediate
+            has_immediate = true;
+          }
+          break;
+        }
+        case relocInfo::virtual_call_type:  // Fall through. They all call resolve_*_call blobs.
+        case relocInfo::opt_virtual_call_type:
+        case relocInfo::static_call_type: {
+          CallRelocation* r = (CallRelocation*)iter.reloc();
+          address dest = r->destination();
+          if (dest == r->addr()) { // possible call via trampoline on Aarch64
+            dest = (address)-1;    // do nothing in this case when loading this relocation
+          }
+          reloc_data[j] = _table->id_for_address(dest, iter, buffer);
+          break;
+        }
+        case relocInfo::trampoline_stub_type: {
+          address dest = ((trampoline_stub_Relocation*)iter.reloc())->destination();
+          reloc_data[j] = _table->id_for_address(dest, iter, buffer);
+          break;
+        }
+        case relocInfo::static_stub_type:
+          break;
+        case relocInfo::runtime_call_type: {
+          // Record offset of runtime destination
+          CallRelocation* r = (CallRelocation*)iter.reloc();
+          address dest = r->destination();
+          if (dest == r->addr()) { // possible call via trampoline on Aarch64
+            dest = (address)-1;    // do nothing in this case when loading this relocation
+          }
+          reloc_data[j] = _table->id_for_address(dest, iter, buffer);
+          break;
+        }
+        case relocInfo::runtime_call_w_cp_type:
+          fatal("runtime_call_w_cp_type unimplemented");
+          break;
+        case relocInfo::external_word_type: {
+          // Record offset of runtime target
+          address target = ((external_word_Relocation*)iter.reloc())->target();
+          reloc_data[j] = _table->id_for_address(target, iter, buffer);
+          break;
+        }
+        case relocInfo::internal_word_type:
+          break;
+        case relocInfo::section_word_type:
+          break;
+        case relocInfo::poll_type:
+          break;
+        case relocInfo::poll_return_type:
+          break;
+        case relocInfo::post_call_nop_type:
+          break;
+        case relocInfo::entry_guard_type:
+          break;
+        default:
+          fatal("relocation %d unimplemented", (int)iter.type());
+          break;
+      }
+      if (log.is_enabled()) {
+        iter.print_current_on(&log);
+      }
+      j++;
+    }
+    assert(j <= (int)reloc_count, "sanity");
+    // Write additional relocation data: uint per relocation
+    uint data_size = reloc_count * sizeof(uint);
+    n = write_bytes(reloc_data, data_size);
+    if (n != data_size) {
+      success = false;
+      break;
+    }
+    if (has_immediate) {
+      // Save information about immediates in this Code Section
+      RelocIterator iter_imm(cs);
+      int j = 0;
+      while (iter_imm.next()) {
+        switch (iter_imm.type()) {
+          case relocInfo::oop_type: {
+            oop_Relocation* r = (oop_Relocation*)iter_imm.reloc();
+            if (r->oop_is_immediate()) {
+              assert(reloc_data[j] == (uint)j, "should be");
+              jobject jo = *(jobject*)(r->oop_addr()); // Handle currently
+              if (!write_oop(jo)) {
+                success = false;
+              }
+            }
+            break;
+          }
+          case relocInfo::metadata_type: {
+            metadata_Relocation* r = (metadata_Relocation*)iter_imm.reloc();
+            if (r->metadata_is_immediate()) {
+              assert(reloc_data[j] == (uint)j, "should be");
+              Metadata* m = r->metadata_value();
+              if (!write_metadata(m)) {
+                success = false;
+              }
+            }
+            break;
+          }
+          default:
+            break;
+        }
+        if (!success) {
+          break;
+        }
+        j++;
+      } // while (iter_imm.next())
+    } // if (has_immediate)
+  } // for(i < SECT_LIMIT)
+  FREE_C_HEAP_ARRAY(uint, reloc_data);
+  return success;
+}
+
+bool SCCache::write_code(CodeBuffer* buffer, uint& code_size) {
+  assert(_write_position == align_up(_write_position, DATA_ALIGNMENT), "%d not aligned to %d", _write_position, DATA_ALIGNMENT);
+  //assert(buffer->blob() != nullptr, "sanity");
+  uint code_offset = _write_position;
+  uint cb_total_size = (uint)buffer->total_content_size();
+  // Write information about Code sections first.
+  SCCodeSection scc_cs[CodeBuffer::SECT_LIMIT];
+  uint scc_cs_size = (uint)(sizeof(SCCodeSection) * CodeBuffer::SECT_LIMIT);
+  uint offset = align_up(scc_cs_size, DATA_ALIGNMENT);
+  uint total_size = 0;
+  for (int i = 0; i < (int)CodeBuffer::SECT_LIMIT; i++) {
+    const CodeSection* cs = buffer->code_section(i);
+    assert(cs->mark() == nullptr, "CodeSection::_mark is not implemented");
+    uint cs_size = (uint)cs->size();
+    scc_cs[i]._size = cs_size;
+    scc_cs[i]._origin_address = (cs_size == 0) ? nullptr : cs->start();
+    scc_cs[i]._offset = (cs_size == 0) ? 0 : (offset + total_size);
+    assert(cs->mark() == nullptr, "CodeSection::_mark is not implemented");
+    total_size += align_up(cs_size, DATA_ALIGNMENT);
+  }
+  uint n = write_bytes(scc_cs, scc_cs_size);
+  if (n != scc_cs_size) {
+    return false;
+  }
+  if (!align_write()) {
+    return false;
+  }
+  assert(_write_position == (code_offset + offset), "%d  != (%d + %d)", _write_position, code_offset, offset);
+  for (int i = 0; i < (int)CodeBuffer::SECT_LIMIT; i++) {
+    const CodeSection* cs = buffer->code_section(i);
+    uint cs_size = (uint)cs->size();
+    if (cs_size == 0) {
+      continue;  // skip trivial section
+    }
+    assert((_write_position - code_offset) == scc_cs[i]._offset, "%d != %d", _write_position, scc_cs[i]._offset);
+    // Write code
+    n = write_bytes(cs->start(), cs_size);
+    if (n != cs_size) {
+      return false;
+    }
+    if (!align_write()) {
+      return false;
+    }
+  }
+  assert((_write_position - code_offset) == (offset + total_size), "(%d - %d) != (%d + %d)", _write_position, code_offset, offset, total_size);
+  code_size = total_size;
+  return true;
+}
+
+bool SCCache::store_exception_blob(CodeBuffer* buffer, int pc_offset) {
+  SCCache* cache = open_for_write();
+  if (cache == nullptr) {
+    return false;
+  }
+  log_info(scc, stubs)("Writing blob '%s' to Startup Code Cache '%s'", buffer->name(), cache->_cache_path);
+
+#ifdef ASSERT
+  LogStreamHandle(Debug, scc, nmethod) log;
+  if (log.is_enabled()) {
+    FlagSetting fs(PrintRelocations, true);
+    buffer->print_on(&log);
+    buffer->decode();
+  }
+#endif
+  if (!cache->align_write()) {
+    return false;
+  }
+  uint entry_position = cache->_write_position;
+
+  // Write pc_offset
+  uint n = cache->write_bytes(&pc_offset, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+
+  // Write name
+  const char* name = buffer->name();
+  uint name_offset = cache->_write_position - entry_position;
+  uint name_size = (uint)strlen(name) + 1; // Includes '/0'
+  n = cache->write_bytes(name, name_size);
+  if (n != name_size) {
+    return false;
+  }
+
+  // Write code section
+  if (!cache->align_write()) {
+    return false;
+  }
+  uint code_offset = cache->_write_position - entry_position;
+  uint code_size = 0;
+  if (!cache->write_code(buffer, code_size)) {
+    return false;
+  }
+  // Write relocInfo array
+  uint reloc_offset = cache->_write_position - entry_position;
+  uint reloc_size = 0;
+  if (!cache->write_relocations(buffer, reloc_size)) {
+    return false;
+  }
+
+  uint entry_size = cache->_write_position - entry_position;
+  SCCEntry* entry = new(cache) SCCEntry(entry_position, entry_size, name_offset, name_size,
+                                          code_offset, code_size, reloc_offset, reloc_size,
+                                          SCCEntry::Blob, (uint32_t)999);
+  log_info(scc, stubs)("Wrote stub '%s' to Startup Code Cache '%s'", name, cache->_cache_path);
+  return true;
+}
+
+DebugInformationRecorder* SCCReader::read_debug_info(OopRecorder* oop_recorder) {
+  uint code_offset = align_up(read_position(), DATA_ALIGNMENT);
+  int data_size  = *(int*)addr(code_offset);
+  code_offset   += sizeof(int);
+  int pcs_length = *(int*)addr(code_offset);
+  code_offset   += sizeof(int);
+
+  log_debug(scc)("======== read DebugInfo [%d, %d]:", data_size, pcs_length);
+
+  // Aligned initial sizes
+  int data_size_align  = align_up(data_size, DATA_ALIGNMENT);
+  int pcs_length_align = pcs_length + 1;
+  assert(sizeof(PcDesc) > DATA_ALIGNMENT, "sanity");
+  DebugInformationRecorder* recorder = new DebugInformationRecorder(oop_recorder, data_size_align, pcs_length);
+
+  copy_bytes(addr(code_offset), recorder->stream()->buffer(), data_size_align);
+  recorder->stream()->set_position(data_size);
+  code_offset += data_size;
+
+  uint pcs_size = pcs_length * sizeof(PcDesc);
+  copy_bytes(addr(code_offset), (address)recorder->pcs(), pcs_size);
+  code_offset += pcs_size;
+  set_read_position(code_offset);
+  return recorder;
+}
+
+bool SCCache::write_debug_info(DebugInformationRecorder* recorder) {
+  if (!align_write()) {
+    return false;
+  }
+  // Don't call data_size() and pcs_size(). They will freeze OopRecorder.
+  int data_size = recorder->stream()->position(); // In bytes
+  uint n = write_bytes(&data_size, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  int pcs_length = recorder->pcs_length(); // In bytes
+  n = write_bytes(&pcs_length, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  n = write_bytes(recorder->stream()->buffer(), data_size);
+  if (n != (uint)data_size) {
+    return false;
+  }
+  uint pcs_size = pcs_length * sizeof(PcDesc);
+  n = write_bytes(recorder->pcs(), pcs_size);
+  if (n != pcs_size) {
+    return false;
+  }
+  return true;
+}
+
+OopMapSet* SCCReader::read_oop_maps() {
+  uint code_offset = read_position();
+  int om_count = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+
+  log_debug(scc)("======== read oop maps [%d]:", om_count);
+
+  OopMapSet* oop_maps = new OopMapSet(om_count);
+  for (int i = 0; i < (int)om_count; i++) {
+    int data_size = *(int*)addr(code_offset);
+    code_offset += sizeof(int);
+
+    OopMap* oop_map = new OopMap(data_size);
+    // Preserve allocated stream
+    CompressedWriteStream* stream = oop_map->write_stream();
+
+    // Read data which overwrites default data
+    copy_bytes(addr(code_offset), (address)oop_map, sizeof(OopMap));
+    code_offset += sizeof(OopMap);
+    stream->set_position(data_size);
+    oop_map->set_write_stream(stream);
+    if (data_size > 0) {
+      copy_bytes(addr(code_offset), (address)(oop_map->data()), (uint)data_size);
+      code_offset += data_size;
+    }
+#ifdef ASSERT
+    oop_map->_locs_length = 0;
+    oop_map->_locs_used   = nullptr;
+#endif
+    oop_maps->add(oop_map);
+  }
+  set_read_position(code_offset);
+  return oop_maps;
+}
+
+bool SCCache::write_oop_maps(OopMapSet* oop_maps) {
+  uint om_count = oop_maps->size();
+  uint n = write_bytes(&om_count, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  for (int i = 0; i < (int)om_count; i++) {
+    OopMap* om = oop_maps->at(i);
+    int data_size = om->data_size();
+    n = write_bytes(&data_size, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+    n = write_bytes(om, sizeof(OopMap));
+    if (n != sizeof(OopMap)) {
+      return false;
+    }
+    n = write_bytes(om->data(), (uint)data_size);
+    if (n != (uint)data_size) {
+      return false;
+    }
+  }
+  return true;
+}
+
+jobject SCCReader::read_oop(JavaThread* thread, const methodHandle& comp_method) {
+  uint code_offset = read_position();
+  oop obj = nullptr;
+  DataKind kind = *(DataKind*)addr(code_offset);
+  code_offset += sizeof(DataKind);
+  set_read_position(code_offset);
+  if (kind == DataKind::Null) {
+    return nullptr;
+  } else if (kind == DataKind::No_Data) {
+    return (jobject)Universe::non_oop_word();
+  } else if (kind == DataKind::Klass || kind == DataKind::Klass_Shared) {
+    Klass* k = read_klass(comp_method, (kind == DataKind::Klass_Shared));
+    if (k == nullptr) {
+      return nullptr;
+    }
+    obj = k->java_mirror();
+    if (obj == nullptr) {
+      set_lookup_failed();
+      log_info(scc)("Lookup failed for java_mirror of klass %s", k->external_name());
+      return nullptr;
+    }
+  } else if (kind == DataKind::Primitive) {
+    code_offset = read_position();
+    int t = *(int*)addr(code_offset);
+    code_offset += sizeof(int);
+    set_read_position(code_offset);
+    BasicType bt = (BasicType)t;
+    obj = java_lang_Class::primitive_mirror(bt);
+    log_info(scc)("%d (L%d): Read primitive type klass: %s", compile_id(), comp_level(), type2name(bt));
+  } else if (kind == DataKind::String_Shared) {
+    code_offset = read_position();
+    int k = *(int*)addr(code_offset);
+    code_offset += sizeof(int);
+    set_read_position(code_offset);
+    obj = CDSAccess::get_archived_object(k);
+  } else if (kind == DataKind::String) {
+    code_offset = read_position();
+    int length = *(int*)addr(code_offset);
+    code_offset += sizeof(int);
+    set_read_position(code_offset);
+    const char* dest = addr(code_offset);
+    set_read_position(code_offset + length);
+    obj = StringTable::intern(&(dest[0]), thread);
+    if (obj == nullptr) {
+      set_lookup_failed();
+      log_info(scc)("%d (L%d): Lookup failed for String %s",
+                       compile_id(), comp_level(), &(dest[0]));
+      return nullptr;
+    }
+    assert(java_lang_String::is_instance(obj), "must be string");
+    log_info(scc)("%d (L%d): Read String: %s", compile_id(), comp_level(), dest);
+  } else if (kind == DataKind::SysLoader) {
+    obj = SystemDictionary::java_system_loader();
+    log_info(scc)("%d (L%d): Read java_system_loader", compile_id(), comp_level());
+  } else if (kind == DataKind::PlaLoader) {
+    obj = SystemDictionary::java_platform_loader();
+    log_info(scc)("%d (L%d): Read java_platform_loader", compile_id(), comp_level());
+  } else if (kind == DataKind::MH_Oop_Shared) {
+    code_offset = read_position();
+    int k = *(int*)addr(code_offset);
+    code_offset += sizeof(int);
+    set_read_position(code_offset);
+    obj = CDSAccess::get_archived_object(k);
+  } else {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Unknown oop's kind: %d",
+                     compile_id(), comp_level(), (int)kind);
+    return nullptr;
+  }
+  return JNIHandles::make_local(thread, obj);
+}
+
+bool SCCReader::read_oops(OopRecorder* oop_recorder, ciMethod* target) {
+  uint code_offset = read_position();
+  int oop_count = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  set_read_position(code_offset);
+  log_debug(scc)("======== read oops [%d]:", oop_count);
+  if (oop_count == 0) {
+    return true;
+  }
+  {
+    VM_ENTRY_MARK;
+    methodHandle comp_method(THREAD, target->get_Method());
+    for (int i = 1; i < oop_count; i++) {
+      jobject jo = read_oop(THREAD, comp_method);
+      if (lookup_failed()) {
+        return false;
+      }
+      if (oop_recorder->is_real(jo)) {
+        oop_recorder->find_index(jo);
+      } else {
+        oop_recorder->allocate_oop_index(jo);
+      }
+      LogStreamHandle(Debug, scc, oops) log;
+      if (log.is_enabled()) {
+        log.print("%d: " INTPTR_FORMAT " ", i, p2i(jo));
+        if (jo == (jobject)Universe::non_oop_word()) {
+          log.print("non-oop word");
+        } else if (jo == nullptr) {
+          log.print("nullptr-oop");
+        } else {
+          JNIHandles::resolve(jo)->print_value_on(&log);
+        }
+        log.cr();
+      }
+    }
+  }
+  return true;
+}
+
+Metadata* SCCReader::read_metadata(const methodHandle& comp_method) {
+  uint code_offset = read_position();
+  Metadata* m = nullptr;
+  DataKind kind = *(DataKind*)addr(code_offset);
+  code_offset += sizeof(DataKind);
+  set_read_position(code_offset);
+  if (kind == DataKind::Null) {
+    m = (Metadata*)nullptr;
+  } else if (kind == DataKind::No_Data) {
+    m = (Metadata*)Universe::non_oop_word();
+  } else if (kind == DataKind::Klass || kind == DataKind::Klass_Shared) {
+    m = (Metadata*)read_klass(comp_method, (kind == DataKind::Klass_Shared));
+  } else if (kind == DataKind::Method || kind == DataKind::Method_Shared) {
+    m = (Metadata*)read_method(comp_method, (kind == DataKind::Method_Shared));
+  } else if (kind == DataKind::MethodCnts) {
+    kind = *(DataKind*)addr(code_offset);
+    bool shared = (kind == DataKind::Method_Shared);
+    assert(kind == DataKind::Method || shared, "Sanity");
+    code_offset += sizeof(DataKind);
+    set_read_position(code_offset);
+    m = (Metadata*)read_method(comp_method, shared);
+    if (m != nullptr) {
+      Method* method = (Method*)m;
+      m = method->get_method_counters(Thread::current());
+      if (m == nullptr) {
+        set_lookup_failed();
+        log_info(scc)("%d (L%d): Failed to get MethodCounters", compile_id(), comp_level());
+      } else {
+        log_info(scc)("%d (L%d): Read MethodCounters : " INTPTR_FORMAT, compile_id(), comp_level(), p2i(m));
+      }
+    }
+  } else {
+    set_lookup_failed();
+    log_info(scc)("%d (L%d): Unknown metadata's kind: %d", compile_id(), comp_level(), (int)kind);
+  }
+  return m;
+}
+
+bool SCCReader::read_metadata(OopRecorder* oop_recorder, ciMethod* target) {
+  uint code_offset = read_position();
+  int metadata_count = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  set_read_position(code_offset);
+
+  log_debug(scc)("======== read metadata [%d]:", metadata_count);
+
+  if (metadata_count == 0) {
+    return true;
+  }
+  {
+    VM_ENTRY_MARK;
+    methodHandle comp_method(THREAD, target->get_Method());
+
+    for (int i = 1; i < metadata_count; i++) {
+      Metadata* m = read_metadata(comp_method);
+      if (lookup_failed()) {
+        return false;
+      }
+      if (oop_recorder->is_real(m)) {
+        oop_recorder->find_index(m);
+      } else {
+        oop_recorder->allocate_metadata_index(m);
+      }
+      LogTarget(Debug, scc, metadata) log;
+      if (log.is_enabled()) {
+        LogStream ls(log);
+        ls.print("%d: " INTPTR_FORMAT " ", i, p2i(m));
+        if (m == (Metadata*)Universe::non_oop_word()) {
+          ls.print("non-metadata word");
+        } else if (m == nullptr) {
+          ls.print("nullptr-oop");
+        } else {
+          Metadata::print_value_on_maybe_null(&ls, m);
+        }
+        ls.cr();
+      }
+    }
+  }
+  return true;
+}
+
+bool SCCache::write_oop(jobject& jo) {
+  DataKind kind;
+  uint n = 0;
+  oop obj = JNIHandles::resolve(jo);
+  if (jo == nullptr) {
+    kind = DataKind::Null;
+    n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+  } else if (jo == (jobject)Universe::non_oop_word()) {
+    kind = DataKind::No_Data;
+    n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+  } else if (java_lang_Class::is_instance(obj)) {
+    if (java_lang_Class::is_primitive(obj)) {
+      int bt = (int)java_lang_Class::primitive_type(obj);
+      kind = DataKind::Primitive;
+      n = write_bytes(&kind, sizeof(int));
+      if (n != sizeof(int)) {
+        return false;
+      }
+      n = write_bytes(&bt, sizeof(int));
+      if (n != sizeof(int)) {
+        return false;
+      }
+      log_info(scc)("%d (L%d): Write primitive type klass: %s", compile_id(), comp_level(), type2name((BasicType)bt));
+    } else {
+      Klass* klass = java_lang_Class::as_Klass(obj);
+      if (!write_klass(klass)) {
+        return false;
+      }
+    }
+  } else if (java_lang_String::is_instance(obj)) { // herere
+    int k = CDSAccess::get_archived_object_permanent_index(obj);  // k >= 0 means obj is a "permanent heap object"
+    if (k >= 0) {
+      kind = DataKind::String_Shared;
+      n = write_bytes(&kind, sizeof(int));
+      if (n != sizeof(int)) {
+        return false;
+      }
+      n = write_bytes(&k, sizeof(int));
+      if (n != sizeof(int)) {
+        return false;
+      }
+      return true;
+    }
+    kind = DataKind::String;
+    n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+    ResourceMark rm;
+    size_t length_sz = 0;
+    const char* string = java_lang_String::as_utf8_string(obj, length_sz);
+    int length = (int)length_sz; // FIXME -- cast
+    length++; // write tailing '/0'
+    n = write_bytes(&length, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+    n = write_bytes(string, (uint)length);
+    if (n != (uint)length) {
+      return false;
+    }
+    log_info(scc)("%d (L%d): Write String: %s", compile_id(), comp_level(), string);
+  } else if (java_lang_Module::is_instance(obj)) {
+    fatal("Module object unimplemented");
+  } else if (java_lang_ClassLoader::is_instance(obj)) {
+    if (obj == SystemDictionary::java_system_loader()) {
+      kind = DataKind::SysLoader;
+      log_info(scc)("%d (L%d): Write ClassLoader: java_system_loader", compile_id(), comp_level());
+    } else if (obj == SystemDictionary::java_platform_loader()) {
+      kind = DataKind::PlaLoader;
+      log_info(scc)("%d (L%d): Write ClassLoader: java_platform_loader", compile_id(), comp_level());
+    } else {
+      fatal("ClassLoader object unimplemented");
+      return false;
+    }
+    n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+  } else { // herere
+    int k = CDSAccess::get_archived_object_permanent_index(obj);  // k >= 0 means obj is a "permanent heap object"
+    if (k >= 0) {
+      kind = DataKind::MH_Oop_Shared;
+      n = write_bytes(&kind, sizeof(int));
+      if (n != sizeof(int)) {
+        return false;
+      }
+      n = write_bytes(&k, sizeof(int));
+      if (n != sizeof(int)) {
+        return false;
+      }
+      return true;
+    }
+    // Unhandled oop - bailout
+    set_lookup_failed();
+    log_info(scc, nmethod)("%d (L%d): Unhandled obj: " PTR_FORMAT " : %s",
+                              compile_id(), comp_level(), p2i(obj), obj->klass()->external_name());
+    return false;
+  }
+  return true;
+}
+
+bool SCCache::write_oops(OopRecorder* oop_recorder) {
+  int oop_count = oop_recorder->oop_count();
+  uint n = write_bytes(&oop_count, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+  log_debug(scc)("======== write oops [%d]:", oop_count);
+
+  for (int i = 1; i < oop_count; i++) { // skip first virtual nullptr
+    jobject jo = oop_recorder->oop_at(i);
+    LogStreamHandle(Info, scc, oops) log;
+    if (log.is_enabled()) {
+      log.print("%d: " INTPTR_FORMAT " ", i, p2i(jo));
+      if (jo == (jobject)Universe::non_oop_word()) {
+        log.print("non-oop word");
+      } else if (jo == nullptr) {
+        log.print("nullptr-oop");
+      } else {
+        JNIHandles::resolve(jo)->print_value_on(&log);
+      }
+      log.cr();
+    }
+    if (!write_oop(jo)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool SCCache::write_metadata(Metadata* m) {
+  uint n = 0;
+  if (m == nullptr) {
+    DataKind kind = DataKind::Null;
+    n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+  } else if (m == (Metadata*)Universe::non_oop_word()) {
+    DataKind kind = DataKind::No_Data;
+    n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+  } else if (m->is_klass()) {
+    if (!write_klass((Klass*)m)) {
+      return false;
+    }
+  } else if (m->is_method()) {
+    if (!write_method((Method*)m)) {
+      return false;
+    }
+  } else if (m->is_methodCounters()) {
+    DataKind kind = DataKind::MethodCnts;
+    n = write_bytes(&kind, sizeof(int));
+    if (n != sizeof(int)) {
+      return false;
+    }
+    if (!write_method(((MethodCounters*)m)->method())) {
+      return false;
+    }
+    log_info(scc)("%d (L%d): Write MethodCounters : " INTPTR_FORMAT, compile_id(), comp_level(), p2i(m));
+  } else { // Not supported
+    fatal("metadata : " INTPTR_FORMAT " unimplemented", p2i(m));
+    return false;
+  }
+  return true;
+}
+
+bool SCCache::write_metadata(OopRecorder* oop_recorder) {
+  int metadata_count = oop_recorder->metadata_count();
+  uint n = write_bytes(&metadata_count, sizeof(int));
+  if (n != sizeof(int)) {
+    return false;
+  }
+
+  log_debug(scc)("======== write metadata [%d]:", metadata_count);
+
+  for (int i = 1; i < metadata_count; i++) { // skip first virtual nullptr
+    Metadata* m = oop_recorder->metadata_at(i);
+    LogStreamHandle(Debug, scc, metadata) log;
+    if (log.is_enabled()) {
+      log.print("%d: " INTPTR_FORMAT " ", i, p2i(m));
+      if (m == (Metadata*)Universe::non_oop_word()) {
+        log.print("non-metadata word");
+      } else if (m == nullptr) {
+        log.print("nullptr-oop");
+      } else {
+        Metadata::print_value_on_maybe_null(&log, m);
+      }
+      log.cr();
+    }
+    if (!write_metadata(m)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool SCCReader::read_dependencies(Dependencies* dependencies) {
+  uint code_offset = read_position();
+  int dependencies_size = *(int*)addr(code_offset);
+
+  log_debug(scc)("======== read dependencies [%d]:", dependencies_size);
+
+  code_offset += sizeof(int);
+  code_offset = align_up(code_offset, DATA_ALIGNMENT);
+  if (dependencies_size > 0) {
+    dependencies->set_content((u_char*)addr(code_offset), dependencies_size);
+  }
+  code_offset += dependencies_size;
+  set_read_position(code_offset);
+  return true;
+}
+
+bool SCCache::load_nmethod(ciEnv* env, ciMethod* target, int entry_bci, AbstractCompiler* compiler, CompLevel comp_level) {
+  TraceTime t1("SC total load time", &_t_totalLoad, enable_timers(), false);
+  CompileTask* task = env->task();
+  SCCEntry* entry = task->scc_entry();
+  bool preload = task->preload();
+  assert(entry != nullptr, "sanity");
+  SCCache* cache = open_for_read();
+  if (cache == nullptr) {
+    return false;
+  }
+  if (log_is_enabled(Info, scc, nmethod)) {
+    uint decomp = (target->method_data() == nullptr) ? 0 : target->method_data()->decompile_count();
+    VM_ENTRY_MARK;
+    ResourceMark rm;
+    methodHandle method(THREAD, target->get_Method());
+    const char* target_name = method->name_and_sig_as_C_string();
+    uint hash = java_lang_String::hash_code((const jbyte*)target_name, (int)strlen(target_name));
+    bool clinit_brs = entry->has_clinit_barriers();
+    log_info(scc, nmethod)("%d (L%d): %s nmethod '%s' (decomp: %d, hash: " UINT32_FORMAT_X_0 "%s%s)",
+                           task->compile_id(), task->comp_level(), (preload ? "Preloading" : "Reading"),
+                           target_name, decomp, hash, (clinit_brs ? ", has clinit barriers" : ""),
+                           (entry->ignore_decompile() ? ", ignore_decomp" : ""));
+  }
+  ReadingMark rdmk;
+  if (rdmk.failed()) {
+    // Cache is closed, cannot touch anything.
+    return false;
+  }
+
+  SCCReader reader(cache, entry, task);
+  bool success = reader.compile(env, target, entry_bci, compiler);
+  if (success) {
+    task->set_num_inlined_bytecodes(entry->num_inlined_bytecodes());
+  } else {
+    entry->set_load_fail();
+  }
+  return success;
+}
+
+SCCReader::SCCReader(SCCache* cache, SCCEntry* entry, CompileTask* task) {
+  _cache = cache;
+  _entry   = entry;
+  _load_buffer = cache->cache_buffer();
+  _read_position = 0;
+  if (task != nullptr) {
+    _compile_id = task->compile_id();
+    _comp_level = task->comp_level();
+    _preload    = task->preload();
+  } else {
+    _compile_id = 0;
+    _comp_level = 0;
+    _preload    = false;
+  }
+  _lookup_failed = false;
+}
+
+bool SCCReader::compile(ciEnv* env, ciMethod* target, int entry_bci, AbstractCompiler* compiler) {
+  uint entry_position = _entry->offset();
+  uint code_offset = entry_position + _entry->code_offset();
+  set_read_position(code_offset);
+
+  // Read flags
+  int flags = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  bool has_monitors      = (flags & 0x1) != 0;
+  bool has_wide_vectors  = (flags & 0x2) != 0;
+  bool has_unsafe_access = (flags & 0x4) != 0;
+  bool has_scoped_access = (flags & 0x8) != 0;
+
+  int orig_pc_offset = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  int frame_size = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+
+  // Read offsets
+  CodeOffsets* offsets = (CodeOffsets*)addr(code_offset);
+  code_offset += sizeof(CodeOffsets);
+
+  // Create Debug Information Recorder to record scopes, oopmaps, etc.
+  OopRecorder* oop_recorder = new OopRecorder(env->arena());
+  env->set_oop_recorder(oop_recorder);
+
+  set_read_position(code_offset);
+
+  // Write OopRecorder data
+  if (!read_oops(oop_recorder, target)) {
+    return false;
+  }
+  if (!read_metadata(oop_recorder, target)) {
+    return false;
+  }
+
+  // Read Debug info
+  DebugInformationRecorder* recorder = read_debug_info(oop_recorder);
+  if (recorder == nullptr) {
+    return false;
+  }
+  env->set_debug_info(recorder);
+
+  // Read Dependencies (compressed already)
+  Dependencies* dependencies = new Dependencies(env);
+  if (!read_dependencies(dependencies)) {
+    return false;
+  }
+  env->set_dependencies(dependencies);
+
+  // Read oop maps
+  OopMapSet* oop_maps = read_oop_maps();
+  if (oop_maps == nullptr) {
+    return false;
+  }
+
+  // Read exception handles
+  code_offset = read_position();
+  int exc_table_length = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  ExceptionHandlerTable handler_table(MAX2(exc_table_length, 4));
+  if (exc_table_length > 0) {
+    handler_table.set_length(exc_table_length);
+    uint exc_table_size = handler_table.size_in_bytes();
+    copy_bytes(addr(code_offset), (address)handler_table.table(), exc_table_size);
+    code_offset += exc_table_size;
+  }
+
+  // Read null check table
+  int nul_chk_length = *(int*)addr(code_offset);
+  code_offset += sizeof(int);
+  ImplicitExceptionTable nul_chk_table;
+  if (nul_chk_length > 0) {
+    nul_chk_table.set_size(nul_chk_length);
+    nul_chk_table.set_len(nul_chk_length);
+    uint nul_chk_size = nul_chk_table.size_in_bytes();
+    copy_bytes(addr(code_offset), (address)nul_chk_table.data(), nul_chk_size - sizeof(implicit_null_entry));
+    code_offset += nul_chk_size;
+  }
+
+  uint reloc_size = _entry->reloc_size();
+  CodeBuffer buffer("Compile::Fill_buffer", _entry->code_size(), reloc_size);
+  buffer.initialize_oop_recorder(oop_recorder);
+
+  const char* name = addr(entry_position + _entry->name_offset());
+
+  // Create fake original CodeBuffer
+  CodeBuffer orig_buffer(name);
+
+  // Read code
+  if (!read_code(&buffer, &orig_buffer, align_up(code_offset, DATA_ALIGNMENT))) {
+    return false;
+  }
+
+  // Read relocations
+  uint reloc_offset = entry_position + _entry->reloc_offset();
+  set_read_position(reloc_offset);
+  if (!read_relocations(&buffer, &orig_buffer, oop_recorder, target)) {
+    return false;
+  }
+
+  log_info(scc, nmethod)("%d (L%d): Read nmethod '%s' from Startup Code Cache '%s'", compile_id(), comp_level(), name, _cache->cache_path());
+#ifdef ASSERT
+  LogStreamHandle(Debug, scc, nmethod) log;
+  if (log.is_enabled()) {
+    FlagSetting fs(PrintRelocations, true);
+    buffer.print_on(&log);
+    buffer.decode();
+  }
+#endif
+
+  if (VerifyCachedCode) {
+    return false;
+  }
+
+  // Register nmethod
+  TraceTime t1("SC total nmethod register time", &_t_totalRegister, enable_timers(), false);
+  env->register_method(target, entry_bci,
+                       offsets, orig_pc_offset,
+                       &buffer, frame_size,
+                       oop_maps, &handler_table,
+                       &nul_chk_table, compiler,
+                       _entry->has_clinit_barriers(),
+                       false,
+                       has_unsafe_access,
+                       has_wide_vectors,
+                       has_monitors,
+                       has_scoped_access,
+                       0, true /* install_code */,
+                       (SCCEntry *)_entry);
+  CompileTask* task = env->task();
+  bool success = task->is_success();
+  if (success) {
+    ((SCCEntry *)_entry)->set_loaded();
+  }
+  return success;
+}
+
+// No concurency for writing to cache file because this method is called from
+// ciEnv::register_method() under MethodCompileQueue_lock and Compile_lock locks.
+SCCEntry* SCCache::store_nmethod(const methodHandle& method,
+                     int comp_id,
+                     int entry_bci,
+                     CodeOffsets* offsets,
+                     int orig_pc_offset,
+                     DebugInformationRecorder* recorder,
+                     Dependencies* dependencies,
+                     CodeBuffer* buffer,
+                     int frame_size,
+                     OopMapSet* oop_maps,
+                     ExceptionHandlerTable* handler_table,
+                     ImplicitExceptionTable* nul_chk_table,
+                     AbstractCompiler* compiler,
+                     CompLevel comp_level,
+                     bool has_clinit_barriers,
+                     bool for_preload,
+                     bool has_unsafe_access,
+                     bool has_wide_vectors,
+                     bool has_monitors,
+                     bool has_scoped_access) {
+  if (!CDSConfig::is_dumping_cached_code()) {
+    return nullptr; // The metadata and heap in the CDS image haven't been finalized yet.
+  }
+  if (entry_bci != InvocationEntryBci) {
+    return nullptr; // No OSR
+  }
+  if (compiler->is_c1() && (comp_level == CompLevel_simple || comp_level == CompLevel_limited_profile)) {
+    // Cache tier1 compilations
+  } else if (!compiler->is_c2()) {
+    return nullptr; // Only C2 now
+  }
+  TraceTime t1("SC total store time", &_t_totalStore, enable_timers(), false);
+  SCCache* cache = open_for_write();
+  if (cache == nullptr) {
+    return nullptr; // Cache file is closed
+  }
+  SCCEntry* entry = cache->write_nmethod(method, comp_id, entry_bci, offsets, orig_pc_offset, recorder, dependencies, buffer,
+                                  frame_size, oop_maps, handler_table, nul_chk_table, compiler, comp_level,
+                                  has_clinit_barriers, for_preload, has_unsafe_access, has_wide_vectors, has_monitors, has_scoped_access);
+  if (entry == nullptr) {
+    log_info(scc, nmethod)("%d (L%d): nmethod store attempt failed", comp_id, (int)comp_level);
+  }
+  return entry;
+}
+
+SCCEntry* SCCache::write_nmethod(const methodHandle& method,
+                                 int comp_id,
+                                 int entry_bci,
+                                 CodeOffsets* offsets,
+                                 int orig_pc_offset,
+                                 DebugInformationRecorder* recorder,
+                                 Dependencies* dependencies,
+                                 CodeBuffer* buffer,
+                                 int frame_size,
+                                 OopMapSet* oop_maps,
+                                 ExceptionHandlerTable* handler_table,
+                                 ImplicitExceptionTable* nul_chk_table,
+                                 AbstractCompiler* compiler,
+                                 CompLevel comp_level,
+                                 bool has_clinit_barriers,
+                                 bool for_preload,
+                                 bool has_unsafe_access,
+                                 bool has_wide_vectors,
+                                 bool has_monitors,
+                                 bool has_scoped_access) {
+//  if (method->is_hidden()) {
+//    ResourceMark rm;
+//    log_info(scc, nmethod)("%d (L%d): Skip hidden method '%s'", comp_id, (int)comp_level, method->name_and_sig_as_C_string());
+//    return nullptr;
+//  }
+  if (buffer->before_expand() != nullptr) {
+    ResourceMark rm;
+    log_info(scc, nmethod)("%d (L%d): Skip nmethod with expanded buffer '%s'", comp_id, (int)comp_level, method->name_and_sig_as_C_string());
+    return nullptr;
+  }
+#ifdef ASSERT
+  LogStreamHandle(Debug, scc, nmethod) log;
+  if (log.is_enabled()) {
+    tty->print_cr(" == store_nmethod");
+    FlagSetting fs(PrintRelocations, true);
+    buffer->print_on(&log);
+    buffer->decode();
+  }
+#endif
+  assert(!has_clinit_barriers || _gen_preload_code, "sanity");
+  Method* m = method();
+  bool method_in_cds = MetaspaceShared::is_in_shared_metaspace((address)m); // herere
+  InstanceKlass* holder = m->method_holder();
+  bool klass_in_cds = holder->is_shared() && !holder->is_shared_unregistered_class();
+  bool builtin_loader = holder->class_loader_data()->is_builtin_class_loader_data();
+  if (!builtin_loader) {
+    ResourceMark rm;
+    log_info(scc, nmethod)("%d (L%d): Skip method '%s' loaded by custom class loader %s", comp_id, (int)comp_level, method->name_and_sig_as_C_string(), holder->class_loader_data()->loader_name());
+    return nullptr;
+  }
+  if (for_preload && !(method_in_cds && klass_in_cds)) {
+    ResourceMark rm;
+    log_info(scc, nmethod)("%d (L%d): Skip method '%s' for preload: not in CDS", comp_id, (int)comp_level, method->name_and_sig_as_C_string());
+    return nullptr;
+  }
+  assert(!for_preload || method_in_cds, "sanity");
+  _for_preload = for_preload;
+  _has_clinit_barriers = has_clinit_barriers;
+
+  if (!align_write()) {
+    return nullptr;
+  }
+  _compile_id = comp_id;
+  _comp_level = (int)comp_level;
+
+  uint entry_position = _write_position;
+
+  uint decomp = (method->method_data() == nullptr) ? 0 : method->method_data()->decompile_count();
+
+  // Is this one-step workflow assembly phase?
+  // In this phase compilation is done based on saved profiling data
+  // without application run. Ignore decompilation counters in such case.
+  // Also ignore it for C1 code because it is decompiled unconditionally
+  // when C2 generated code is published.
+  bool ignore_decompile = (comp_level == CompLevel_limited_profile) ||
+                          CDSConfig::is_dumping_final_static_archive();
+
+  // Write name
+  uint name_offset = 0;
+  uint name_size   = 0;
+  uint hash = 0;
+  uint n;
+  {
+    ResourceMark rm;
+    const char* name   = method->name_and_sig_as_C_string();
+    log_info(scc, nmethod)("%d (L%d): Writing nmethod '%s' (comp level: %d, decomp: %d%s%s) to Startup Code Cache '%s'",
+                           comp_id, (int)comp_level, name, comp_level, decomp,
+                           (ignore_decompile ? ", ignore_decomp" : ""),
+                           (has_clinit_barriers ? ", has clinit barriers" : ""), _cache_path);
+
+    LogStreamHandle(Info, scc, loader) log;
+    if (log.is_enabled()) {
+      oop loader = holder->class_loader();
+      oop domain = holder->protection_domain();
+      log.print("Holder: ");
+      holder->print_value_on(&log);
+      log.print(" loader: ");
+      if (loader == nullptr) {
+        log.print("nullptr");
+      } else {
+        loader->print_value_on(&log);
+      }
+      log.print(" domain: ");
+      if (domain == nullptr) {
+        log.print("nullptr");
+      } else {
+        domain->print_value_on(&log);
+      }
+      log.cr();
+    }
+    name_offset = _write_position  - entry_position;
+    name_size   = (uint)strlen(name) + 1; // Includes '/0'
+    n = write_bytes(name, name_size);
+    if (n != name_size) {
+      return nullptr;
+    }
+    hash = java_lang_String::hash_code((const jbyte*)name, (int)strlen(name));
+  }
+
+  if (!align_write()) {
+    return nullptr;
+  }
+
+  uint code_offset = _write_position - entry_position;
+
+  int flags = (has_scoped_access ? 0x8 : 0) |
+              (has_unsafe_access ? 0x4 : 0) |
+              (has_wide_vectors  ? 0x2 : 0) |
+              (has_monitors      ? 0x1 : 0);
+  n = write_bytes(&flags, sizeof(int));
+  if (n != sizeof(int)) {
+    return nullptr;
+  }
+
+  n = write_bytes(&orig_pc_offset, sizeof(int));
+  if (n != sizeof(int)) {
+    return nullptr;
+  }
+
+  n = write_bytes(&frame_size, sizeof(int));
+  if (n != sizeof(int)) {
+    return nullptr;
+  }
+
+  // Write offsets
+  n = write_bytes(offsets, sizeof(CodeOffsets));
+  if (n != sizeof(CodeOffsets)) {
+    return nullptr;
+  }
+
+  // Write OopRecorder data
+  if (!write_oops(buffer->oop_recorder())) {
+    if (lookup_failed() && !failed()) {
+      // Skip this method and reposition file
+      set_write_position(entry_position);
+    }
+    return nullptr;
+  }
+  if (!write_metadata(buffer->oop_recorder())) {
+    if (lookup_failed() && !failed()) {
+      // Skip this method and reposition file
+      set_write_position(entry_position);
+    }
+    return nullptr;
+  }
+
+  // Write Debug info
+  if (!write_debug_info(recorder)) {
+    return nullptr;
+  }
+  // Write Dependencies
+  int dependencies_size = (int)dependencies->size_in_bytes();
+  n = write_bytes(&dependencies_size, sizeof(int));
+  if (n != sizeof(int)) {
+    return nullptr;
+  }
+  if (!align_write()) {
+    return nullptr;
+  }
+  n = write_bytes(dependencies->content_bytes(), dependencies_size);
+  if (n != (uint)dependencies_size) {
+    return nullptr;
+  }
+
+  // Write oop maps
+  if (!write_oop_maps(oop_maps)) {
+    return nullptr;
+  }
+
+  // Write exception handles
+  int exc_table_length = handler_table->length();
+  n = write_bytes(&exc_table_length, sizeof(int));
+  if (n != sizeof(int)) {
+    return nullptr;
+  }
+  uint exc_table_size = handler_table->size_in_bytes();
+  n = write_bytes(handler_table->table(), exc_table_size);
+  if (n != exc_table_size) {
+    return nullptr;
+  }
+
+  // Write null check table
+  int nul_chk_length = nul_chk_table->len();
+  n = write_bytes(&nul_chk_length, sizeof(int));
+  if (n != sizeof(int)) {
+    return nullptr;
+  }
+  uint nul_chk_size = nul_chk_table->size_in_bytes();
+  n = write_bytes(nul_chk_table->data(), nul_chk_size);
+  if (n != nul_chk_size) {
+    return nullptr;
+  }
+
+  // Write code section
+  if (!align_write()) {
+    return nullptr;
+  }
+  uint code_size = 0;
+  if (!write_code(buffer, code_size)) {
+    return nullptr;
+  }
+  // Write relocInfo array
+  uint reloc_offset = _write_position - entry_position;
+  uint reloc_size = 0;
+  if (!write_relocations(buffer, reloc_size)) {
+    if (lookup_failed() && !failed()) {
+      // Skip this method and reposition file
+      set_write_position(entry_position);
+    }
+    return nullptr;
+  }
+  uint entry_size = _write_position - entry_position;
+
+  SCCEntry* entry = new (this) SCCEntry(entry_position, entry_size, name_offset, name_size,
+                                        code_offset, code_size, reloc_offset, reloc_size,
+                                        SCCEntry::Code, hash, (uint)comp_level, (uint)comp_id, decomp,
+                                        has_clinit_barriers, _for_preload, ignore_decompile);
+  if (method_in_cds) {
+    entry->set_method(m);
+  }
+#ifdef ASSERT
+  if (has_clinit_barriers || _for_preload) {
+    assert(for_preload, "sanity");
+    assert(entry->method() != nullptr, "sanity");
+  }
+#endif
+  {
+    ResourceMark rm;
+    const char* name   = method->name_and_sig_as_C_string();
+    log_info(scc, nmethod)("%d (L%d): Wrote nmethod '%s'%s to Startup Code Cache '%s'",
+                           comp_id, (int)comp_level, name, (_for_preload ? " (for preload)" : ""), _cache_path);
+  }
+  if (VerifyCachedCode) {
+    return nullptr;
+  }
+  return entry;
+}
+
+static void print_helper1(outputStream* st, const char* name, int count) {
+  if (count > 0) {
+    st->print(" %s=%d", name, count);
+  }
+}
+static void print_helper(outputStream* st, const char* name, int stats[6+3][6], int idx) {
+  int total = stats[idx][0];
+  if (total > 0) {
+    st->print("  %s:", name);
+    print_helper1(st, "total",               stats[idx][0]);
+    //print_helper1(st, "for_preload",         stats[idx][2]); // implied by Tier5
+    print_helper1(st, "loaded",              stats[idx][3]);
+    print_helper1(st, "invalidated",         stats[idx][4]);
+    print_helper1(st, "failed",              stats[idx][5]);
+    print_helper1(st, "has_clinit_barriers", stats[idx][1]);
+    st->cr();
+  }
+}
+
+void SCCache::print_statistics_on(outputStream* st) {
+  SCCache* cache = open_for_read();
+  if (cache != nullptr) {
+    ReadingMark rdmk;
+    if (rdmk.failed()) {
+      // Cache is closed, cannot touch anything.
+      return;
+    }
+
+    uint count = cache->_load_header->entries_count();
+    uint* search_entries = (uint*)cache->addr(cache->_load_header->entries_offset()); // [id, index]
+    SCCEntry* load_entries = (SCCEntry*)(search_entries + 2 * count);
+
+    int stats[6 + 3][6] = {0};
+    for (uint i = 0; i < count; i++) {
+      int index = search_entries[2*i + 1];
+      SCCEntry* entry = &(load_entries[index]);
+
+      int lvl = entry->kind();
+      if (entry->kind() == SCCEntry::Code) {
+        lvl += entry->comp_level() + (entry->for_preload() ? 1 : 0);
+      }
+      ++stats[lvl][0]; // total
+      if (entry->has_clinit_barriers()) {
+        ++stats[lvl][1];
+      }
+      if (entry->for_preload()) {
+        ++stats[lvl][2];
+      }
+      if (entry->is_loaded()) {
+        ++stats[lvl][3];
+      }
+      if (entry->not_entrant()) {
+        ++stats[lvl][4];
+      }
+      if (entry->load_fail()) {
+        ++stats[lvl][5];
+      }
+    }
+
+    print_helper(st, "None", stats, SCCEntry::None);
+    print_helper(st, "Stub", stats, SCCEntry::Stub);
+    print_helper(st, "Blob", stats, SCCEntry::Blob);
+    for (int lvl = 0; lvl <= CompLevel_full_optimization + 1; lvl++) {
+      ResourceMark rm;
+      stringStream ss;
+      ss.print("SC T%d", lvl);
+      print_helper(st, ss.freeze(), stats, SCCEntry::Code + lvl);
+    }
+
+  } else {
+    st->print_cr("failed to open SCA at %s", CachedCodeFile);
+  }
+}
+
+void SCCache::print_on(outputStream* st) {
+  SCCache* cache = open_for_read();
+  if (cache != nullptr) {
+    ReadingMark rdmk;
+    if (rdmk.failed()) {
+      // Cache is closed, cannot touch anything.
+      return;
+    }
+
+    uint count = cache->_load_header->entries_count();
+    uint* search_entries = (uint*)cache->addr(cache->_load_header->entries_offset()); // [id, index]
+    SCCEntry* load_entries = (SCCEntry*)(search_entries + 2 * count);
+
+    for (uint i = 0; i < count; i++) {
+      int index = search_entries[2*i + 1];
+      SCCEntry* entry = &(load_entries[index]);
+
+      st->print_cr("%4u: %4u: K%u L%u offset=%u decompile=%u size=%u code_size=%u%s%s%s%s",
+                i, index, entry->kind(), entry->comp_level(), entry->offset(),
+                entry->decompile(), entry->size(), entry->code_size(),
+                entry->has_clinit_barriers() ? " has_clinit_barriers" : "",
+                entry->for_preload()         ? " for_preload"         : "",
+                entry->is_loaded()           ? " loaded"              : "",
+                entry->not_entrant()         ? " not_entrant"         : "");
+      st->print_raw("         ");
+      SCCReader reader(cache, entry, nullptr);
+      reader.print_on(st);
+    }
+  } else {
+    st->print_cr("failed to open SCA at %s", CachedCodeFile);
+  }
+}
+
+void SCCache::print_unused_entries_on(outputStream* st) {
+  LogStreamHandle(Info, scc, init) info;
+  if (info.is_enabled()) {
+    SCCache::iterate([&](SCCEntry* entry) {
+      if (!entry->is_loaded()) {
+        MethodTrainingData* mtd = MethodTrainingData::lookup_for(entry->method());
+        if (mtd != nullptr) {
+          if (mtd->has_holder()) {
+            if (mtd->holder()->method_holder()->is_initialized()) {
+              ResourceMark rm;
+              mtd->iterate_all_compiles([&](CompileTrainingData* ctd) {
+                if ((uint)ctd->level() == entry->comp_level()) {
+                  if (ctd->init_deps_left() == 0) {
+                    nmethod* nm = mtd->holder()->code();
+                    if (nm == nullptr) {
+                      if (mtd->holder()->queued_for_compilation()) {
+                        return; // scheduled for compilation
+                      }
+                    } else if ((uint)nm->comp_level() >= entry->comp_level()) {
+                      return; // already online compiled and superseded by a more optimal method
+                    }
+                    info.print("SCC entry not loaded: ");
+                    ctd->print_on(&info);
+                    info.cr();
+                  }
+                }
+              });
+            } else {
+              // not yet initialized
+            }
+          } else {
+            info.print("SCC entry doesn't have a holder: ");
+            mtd->print_on(&info);
+            info.cr();
+          }
+        }
+      }
+    });
+  }
+}
+
+void SCCReader::print_on(outputStream* st) {
+  uint entry_position = _entry->offset();
+  set_read_position(entry_position);
+
+  // Read name
+  uint name_offset = entry_position + _entry->name_offset();
+  uint name_size = _entry->name_size(); // Includes '/0'
+  const char* name = addr(name_offset);
+
+  st->print_cr("  name: %s", name);
+}
+
+#define _extrs_max 80
+#define _stubs_max 120
+#define _blobs_max 100
+#define _shared_blobs_max 24
+#define _C2_blobs_max 25
+#define _C1_blobs_max (_blobs_max - _shared_blobs_max - _C2_blobs_max)
+#define _all_max 300
+
+#define SET_ADDRESS(type, addr)                           \
+  {                                                       \
+    type##_addr[type##_length++] = (address) (addr);      \
+    assert(type##_length <= type##_max, "increase size"); \
+  }
+
+static bool initializing = false;
+void SCAddressTable::init() {
+  if (_complete || initializing) return; // Done already
+  initializing = true;
+  _extrs_addr = NEW_C_HEAP_ARRAY(address, _extrs_max, mtCode);
+  _stubs_addr = NEW_C_HEAP_ARRAY(address, _stubs_max, mtCode);
+  _blobs_addr = NEW_C_HEAP_ARRAY(address, _blobs_max, mtCode);
+
+  // Divide _blobs_addr array to chunks because they could be initialized in parrallel
+  _C2_blobs_addr = _blobs_addr + _shared_blobs_max;// C2 blobs addresses stored after shared blobs
+  _C1_blobs_addr = _C2_blobs_addr + _C2_blobs_max; // C1 blobs addresses stored after C2 blobs
+
+  _extrs_length = 0;
+  _stubs_length = 0;
+  _blobs_length = 0;       // for shared blobs
+  _C1_blobs_length = 0;
+  _C2_blobs_length = 0;
+  _final_blobs_length = 0; // Depends on numnber of C1 blobs
+
+  // Runtime methods
+#ifdef COMPILER2
+  SET_ADDRESS(_extrs, OptoRuntime::handle_exception_C);
+#endif
+#ifdef COMPILER1
+  SET_ADDRESS(_extrs, Runtime1::is_instance_of);
+  SET_ADDRESS(_extrs, Runtime1::trace_block_entry);
+#endif
+
+  SET_ADDRESS(_extrs, CompressedOops::base_addr());
+#if INCLUDE_G1GC
+  SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_post_entry);
+  SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_pre_entry);
+#endif
+
+#if INCLUDE_SHENANDOAHGC
+  SET_ADDRESS(_extrs, ShenandoahRuntime::arraycopy_barrier_oop);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::arraycopy_barrier_narrow_oop);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::write_ref_field_pre);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::clone_barrier);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_strong);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_strong_narrow);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_weak);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_weak_narrow);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom);
+  SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow);
+#endif
+
+  SET_ADDRESS(_extrs, SharedRuntime::complete_monitor_unlocking_C);
+  SET_ADDRESS(_extrs, SharedRuntime::enable_stack_reserved_zone);
+#if defined(AMD64) && !defined(ZERO)
+  SET_ADDRESS(_extrs, SharedRuntime::montgomery_multiply);
+  SET_ADDRESS(_extrs, SharedRuntime::montgomery_square);
+#endif // AMD64
+  SET_ADDRESS(_extrs, SharedRuntime::d2f);
+  SET_ADDRESS(_extrs, SharedRuntime::d2i);
+  SET_ADDRESS(_extrs, SharedRuntime::d2l);
+  SET_ADDRESS(_extrs, SharedRuntime::dcos);
+  SET_ADDRESS(_extrs, SharedRuntime::dexp);
+  SET_ADDRESS(_extrs, SharedRuntime::dlog);
+  SET_ADDRESS(_extrs, SharedRuntime::dlog10);
+  SET_ADDRESS(_extrs, SharedRuntime::dpow);
+  SET_ADDRESS(_extrs, SharedRuntime::dsin);
+  SET_ADDRESS(_extrs, SharedRuntime::dtan);
+  SET_ADDRESS(_extrs, SharedRuntime::f2i);
+  SET_ADDRESS(_extrs, SharedRuntime::f2l);
+#ifndef ZERO
+  SET_ADDRESS(_extrs, SharedRuntime::drem);
+  SET_ADDRESS(_extrs, SharedRuntime::frem);
+#endif
+  SET_ADDRESS(_extrs, SharedRuntime::l2d);
+  SET_ADDRESS(_extrs, SharedRuntime::l2f);
+  SET_ADDRESS(_extrs, SharedRuntime::ldiv);
+  SET_ADDRESS(_extrs, SharedRuntime::lmul);
+  SET_ADDRESS(_extrs, SharedRuntime::lrem);
+#if INCLUDE_JVMTI
+  SET_ADDRESS(_extrs, &JvmtiExport::_should_notify_object_alloc);
+#endif /* INCLUDE_JVMTI */
+  BarrierSet* bs = BarrierSet::barrier_set();
+  if (bs->is_a(BarrierSet::CardTableBarrierSet)) {
+    SET_ADDRESS(_extrs, ci_card_table_address_as<address>());
+  }
+  SET_ADDRESS(_extrs, ThreadIdentifier::unsafe_offset());
+  SET_ADDRESS(_extrs, Thread::current);
+
+  SET_ADDRESS(_extrs, os::javaTimeMillis);
+  SET_ADDRESS(_extrs, os::javaTimeNanos);
+
+#if INCLUDE_JVMTI
+  SET_ADDRESS(_extrs, &JvmtiVTMSTransitionDisabler::_VTMS_notify_jvmti_events);
+#endif /* INCLUDE_JVMTI */
+  SET_ADDRESS(_extrs, StubRoutines::crc_table_addr());
+#ifndef PRODUCT
+  SET_ADDRESS(_extrs, &SharedRuntime::_partial_subtype_ctr);
+  SET_ADDRESS(_extrs, JavaThread::verify_cross_modify_fence_failure);
+#endif
+
+#ifndef ZERO
+#if defined(AMD64) || defined(AARCH64) || defined(RISCV64)
+  SET_ADDRESS(_extrs, MacroAssembler::debug64);
+#endif
+#if defined(AMD64)
+  SET_ADDRESS(_extrs, StubRoutines::x86::arrays_hashcode_powers_of_31());
+#endif
+#endif
+
+#ifdef COMPILER1
+#ifdef X86
+  SET_ADDRESS(_extrs, LIR_Assembler::float_signmask_pool);
+  SET_ADDRESS(_extrs, LIR_Assembler::double_signmask_pool);
+  SET_ADDRESS(_extrs, LIR_Assembler::float_signflip_pool);
+  SET_ADDRESS(_extrs, LIR_Assembler::double_signflip_pool);
+#endif
+#endif
+
+  // addresses of fields in AOT runtime constants area
+  address* p = AOTRuntimeConstants::field_addresses_list();
+  while (*p != nullptr) {
+    SET_ADDRESS(_extrs, *p++);
+  }
+  // Stubs
+  SET_ADDRESS(_stubs, StubRoutines::method_entry_barrier());
+  SET_ADDRESS(_stubs, StubRoutines::forward_exception_entry());
+/*
+  SET_ADDRESS(_stubs, StubRoutines::throw_AbstractMethodError_entry());
+  SET_ADDRESS(_stubs, StubRoutines::throw_IncompatibleClassChangeError_entry());
+  SET_ADDRESS(_stubs, StubRoutines::throw_NullPointerException_at_call_entry());
+  SET_ADDRESS(_stubs, StubRoutines::throw_StackOverflowError_entry());
+  SET_ADDRESS(_stubs, StubRoutines::throw_delayed_StackOverflowError_entry());
+*/
+  SET_ADDRESS(_stubs, StubRoutines::atomic_xchg_entry());
+  SET_ADDRESS(_stubs, StubRoutines::atomic_cmpxchg_entry());
+  SET_ADDRESS(_stubs, StubRoutines::atomic_cmpxchg_long_entry());
+  SET_ADDRESS(_stubs, StubRoutines::atomic_add_entry());
+  SET_ADDRESS(_stubs, StubRoutines::fence_entry());
+
+  SET_ADDRESS(_stubs, StubRoutines::cont_thaw());
+  SET_ADDRESS(_stubs, StubRoutines::cont_returnBarrier());
+  SET_ADDRESS(_stubs, StubRoutines::cont_returnBarrierExc());
+
+  JFR_ONLY(SET_ADDRESS(_stubs, SharedRuntime::jfr_write_checkpoint());)
+
+
+  SET_ADDRESS(_stubs, StubRoutines::jbyte_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::jshort_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::jint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::jlong_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::_oop_arraycopy);
+  SET_ADDRESS(_stubs, StubRoutines::_oop_arraycopy_uninit);
+
+  SET_ADDRESS(_stubs, StubRoutines::jbyte_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::jshort_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::jint_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::jlong_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::_oop_disjoint_arraycopy);
+  SET_ADDRESS(_stubs, StubRoutines::_oop_disjoint_arraycopy_uninit);
+
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jbyte_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jshort_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jlong_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::_arrayof_oop_arraycopy);
+  SET_ADDRESS(_stubs, StubRoutines::_arrayof_oop_arraycopy_uninit);
+
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jbyte_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jshort_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jint_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jlong_disjoint_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::_arrayof_oop_disjoint_arraycopy);
+  SET_ADDRESS(_stubs, StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit);
+
+  SET_ADDRESS(_stubs, StubRoutines::_checkcast_arraycopy);
+  SET_ADDRESS(_stubs, StubRoutines::_checkcast_arraycopy_uninit);
+
+  SET_ADDRESS(_stubs, StubRoutines::unsafe_arraycopy());
+  SET_ADDRESS(_stubs, StubRoutines::generic_arraycopy());
+
+  SET_ADDRESS(_stubs, StubRoutines::jbyte_fill());
+  SET_ADDRESS(_stubs, StubRoutines::jshort_fill());
+  SET_ADDRESS(_stubs, StubRoutines::jint_fill());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jbyte_fill());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jshort_fill());
+  SET_ADDRESS(_stubs, StubRoutines::arrayof_jint_fill());
+
+  SET_ADDRESS(_stubs, StubRoutines::data_cache_writeback());
+  SET_ADDRESS(_stubs, StubRoutines::data_cache_writeback_sync());
+
+  SET_ADDRESS(_stubs, StubRoutines::aescrypt_encryptBlock());
+  SET_ADDRESS(_stubs, StubRoutines::aescrypt_decryptBlock());
+  SET_ADDRESS(_stubs, StubRoutines::cipherBlockChaining_encryptAESCrypt());
+  SET_ADDRESS(_stubs, StubRoutines::cipherBlockChaining_decryptAESCrypt());
+  SET_ADDRESS(_stubs, StubRoutines::electronicCodeBook_encryptAESCrypt());
+  SET_ADDRESS(_stubs, StubRoutines::electronicCodeBook_decryptAESCrypt());
+  SET_ADDRESS(_stubs, StubRoutines::poly1305_processBlocks());
+  SET_ADDRESS(_stubs, StubRoutines::counterMode_AESCrypt());
+  SET_ADDRESS(_stubs, StubRoutines::ghash_processBlocks());
+  SET_ADDRESS(_stubs, StubRoutines::chacha20Block());
+  SET_ADDRESS(_stubs, StubRoutines::base64_encodeBlock());
+  SET_ADDRESS(_stubs, StubRoutines::base64_decodeBlock());
+  SET_ADDRESS(_stubs, StubRoutines::md5_implCompress());
+  SET_ADDRESS(_stubs, StubRoutines::md5_implCompressMB());
+  SET_ADDRESS(_stubs, StubRoutines::sha1_implCompress());
+  SET_ADDRESS(_stubs, StubRoutines::sha1_implCompressMB());
+  SET_ADDRESS(_stubs, StubRoutines::sha256_implCompress());
+  SET_ADDRESS(_stubs, StubRoutines::sha256_implCompressMB());
+  SET_ADDRESS(_stubs, StubRoutines::sha512_implCompress());
+  SET_ADDRESS(_stubs, StubRoutines::sha512_implCompressMB());
+  SET_ADDRESS(_stubs, StubRoutines::sha3_implCompress());
+  SET_ADDRESS(_stubs, StubRoutines::sha3_implCompressMB());
+
+  SET_ADDRESS(_stubs, StubRoutines::updateBytesCRC32());
+
+  SET_ADDRESS(_stubs, StubRoutines::crc32c_table_addr());
+  SET_ADDRESS(_stubs, StubRoutines::updateBytesCRC32C());
+  SET_ADDRESS(_stubs, StubRoutines::updateBytesAdler32());
+
+  SET_ADDRESS(_stubs, StubRoutines::multiplyToLen());
+  SET_ADDRESS(_stubs, StubRoutines::squareToLen());
+  SET_ADDRESS(_stubs, StubRoutines::mulAdd());
+  SET_ADDRESS(_stubs, StubRoutines::montgomeryMultiply());
+  SET_ADDRESS(_stubs, StubRoutines::montgomerySquare());
+  SET_ADDRESS(_stubs, StubRoutines::bigIntegerRightShift());
+  SET_ADDRESS(_stubs, StubRoutines::bigIntegerLeftShift());
+  SET_ADDRESS(_stubs, StubRoutines::galoisCounterMode_AESCrypt());
+
+  SET_ADDRESS(_stubs, StubRoutines::vectorizedMismatch());
+
+  SET_ADDRESS(_stubs, StubRoutines::dexp());
+  SET_ADDRESS(_stubs, StubRoutines::dlog());
+  SET_ADDRESS(_stubs, StubRoutines::dlog10());
+  SET_ADDRESS(_stubs, StubRoutines::dpow());
+  SET_ADDRESS(_stubs, StubRoutines::dsin());
+  SET_ADDRESS(_stubs, StubRoutines::dcos());
+  SET_ADDRESS(_stubs, StubRoutines::dlibm_reduce_pi04l());
+  SET_ADDRESS(_stubs, StubRoutines::dlibm_sin_cos_huge());
+  SET_ADDRESS(_stubs, StubRoutines::dlibm_tan_cot_huge());
+  SET_ADDRESS(_stubs, StubRoutines::dtan());
+
+  SET_ADDRESS(_stubs, StubRoutines::f2hf_adr());
+  SET_ADDRESS(_stubs, StubRoutines::hf2f_adr());
+
+#if defined(AMD64) && !defined(ZERO)
+  SET_ADDRESS(_stubs, StubRoutines::x86::d2i_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::x86::f2i_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::x86::d2l_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::x86::f2l_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::x86::float_sign_mask());
+  SET_ADDRESS(_stubs, StubRoutines::x86::float_sign_flip());
+  SET_ADDRESS(_stubs, StubRoutines::x86::double_sign_mask());
+  SET_ADDRESS(_stubs, StubRoutines::x86::double_sign_flip());
+  SET_ADDRESS(_stubs, StubRoutines::x86::vector_popcount_lut());
+  SET_ADDRESS(_stubs, StubRoutines::x86::vector_float_sign_mask());
+  SET_ADDRESS(_stubs, StubRoutines::x86::vector_float_sign_flip());
+  SET_ADDRESS(_stubs, StubRoutines::x86::vector_double_sign_mask());
+  SET_ADDRESS(_stubs, StubRoutines::x86::vector_double_sign_flip());
+  // The iota indices are ordered by type B/S/I/L/F/D, and the offset between two types is 64.
+  // See C2_MacroAssembler::load_iota_indices().
+  for (int i = 0; i < 6; i++) {
+    SET_ADDRESS(_stubs, StubRoutines::x86::vector_iota_indices() + i * 64);
+  }
+#endif
+#if defined(AARCH64) && !defined(ZERO)
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::d2i_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::f2i_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::d2l_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::f2l_fixup());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::float_sign_mask());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::float_sign_flip());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::double_sign_mask());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::double_sign_flip());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::zero_blocks());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::count_positives());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::count_positives_long());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::large_array_equals());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::compare_long_string_LL());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::compare_long_string_UU());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::compare_long_string_LU());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::compare_long_string_UL());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::string_indexof_linear_ul());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::string_indexof_linear_ll());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::string_indexof_linear_uu());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::large_byte_array_inflate());
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::spin_wait());
+
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::large_arrays_hashcode(T_BOOLEAN));
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::large_arrays_hashcode(T_BYTE));
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::large_arrays_hashcode(T_SHORT));
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::large_arrays_hashcode(T_CHAR));
+  SET_ADDRESS(_stubs, StubRoutines::aarch64::large_arrays_hashcode(T_INT));
+#endif
+
+  // Blobs
+  SET_ADDRESS(_blobs, SharedRuntime::get_handle_wrong_method_stub());
+  SET_ADDRESS(_blobs, SharedRuntime::get_ic_miss_stub());
+  SET_ADDRESS(_blobs, SharedRuntime::get_resolve_opt_virtual_call_stub());
+  SET_ADDRESS(_blobs, SharedRuntime::get_resolve_virtual_call_stub());
+  SET_ADDRESS(_blobs, SharedRuntime::get_resolve_static_call_stub());
+  SET_ADDRESS(_blobs, SharedRuntime::deopt_blob()->entry_point());
+  SET_ADDRESS(_blobs, SharedRuntime::polling_page_safepoint_handler_blob()->entry_point());
+  SET_ADDRESS(_blobs, SharedRuntime::polling_page_return_handler_blob()->entry_point());
+#ifdef COMPILER2
+  SET_ADDRESS(_blobs, SharedRuntime::polling_page_vectors_safepoint_handler_blob()->entry_point());
+#endif
+
+  SET_ADDRESS(_blobs, SharedRuntime::throw_AbstractMethodError_entry());
+  SET_ADDRESS(_blobs, SharedRuntime::throw_IncompatibleClassChangeError_entry());
+  SET_ADDRESS(_blobs, SharedRuntime::throw_NullPointerException_at_call_entry());
+  SET_ADDRESS(_blobs, SharedRuntime::throw_StackOverflowError_entry());
+  SET_ADDRESS(_blobs, SharedRuntime::throw_delayed_StackOverflowError_entry());
+
+  assert(_blobs_length <= _shared_blobs_max, "increase _shared_blobs_max to %d", _blobs_length);
+  _final_blobs_length = _blobs_length;
+  _complete = true;
+  log_info(scc,init)("External addresses and stubs recorded");
+}
+
+void SCAddressTable::init_opto() {
+#ifdef COMPILER2
+  // OptoRuntime Blobs
+  SET_ADDRESS(_C2_blobs, OptoRuntime::uncommon_trap_blob()->entry_point());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::exception_blob()->entry_point());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::new_instance_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::new_array_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::new_array_nozero_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::multianewarray2_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::multianewarray3_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::multianewarray4_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::multianewarray5_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::multianewarrayN_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::vtable_must_compile_stub());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::complete_monitor_locking_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::monitor_notify_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::monitor_notifyAll_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::rethrow_stub());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::slow_arraycopy_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::register_finalizer_Java());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::class_init_barrier_Java());
+#if INCLUDE_JVMTI
+  SET_ADDRESS(_C2_blobs, OptoRuntime::notify_jvmti_vthread_start());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::notify_jvmti_vthread_end());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::notify_jvmti_vthread_mount());
+  SET_ADDRESS(_C2_blobs, OptoRuntime::notify_jvmti_vthread_unmount());
+#endif /* INCLUDE_JVMTI */
+#endif
+
+  assert(_C2_blobs_length <= _C2_blobs_max, "increase _C2_blobs_max to %d", _C2_blobs_length);
+  _final_blobs_length = MAX2(_final_blobs_length, (_shared_blobs_max + _C2_blobs_length));
+  _opto_complete = true;
+  log_info(scc,init)("OptoRuntime Blobs recorded");
+}
+
+void SCAddressTable::init_c1() {
+#ifdef COMPILER1
+  // Runtime1 Blobs
+  for (int i = 0; i < (int)(C1StubId::NUM_STUBIDS); i++) {
+    C1StubId id = (C1StubId)i;
+    if (Runtime1::blob_for(id) == nullptr) {
+      log_info(scc, init)("C1 blob %s is missing", Runtime1::name_for(id));
+      continue;
+    }
+    if (Runtime1::entry_for(id) == nullptr) {
+      log_info(scc, init)("C1 blob %s is missing entry", Runtime1::name_for(id));
+      continue;
+    }
+    address entry = Runtime1::entry_for(id);
+    SET_ADDRESS(_C1_blobs, entry);
+  }
+#if INCLUDE_G1GC
+  if (UseG1GC) {
+    G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
+    address entry = bs->pre_barrier_c1_runtime_code_blob()->code_begin();
+    SET_ADDRESS(_C1_blobs, entry);
+    entry = bs->post_barrier_c1_runtime_code_blob()->code_begin();
+    SET_ADDRESS(_C1_blobs, entry);
+  }
+#endif // INCLUDE_G1GC
+#if INCLUDE_ZGC
+  if (UseZGC) {
+    ZBarrierSetC1* bs = (ZBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
+    SET_ADDRESS(_C1_blobs, bs->_load_barrier_on_oop_field_preloaded_runtime_stub);
+    SET_ADDRESS(_C1_blobs, bs->_load_barrier_on_weak_oop_field_preloaded_runtime_stub);
+    SET_ADDRESS(_C1_blobs, bs->_store_barrier_on_oop_field_with_healing);
+    SET_ADDRESS(_C1_blobs, bs->_store_barrier_on_oop_field_without_healing);
+  }
+#endif // INCLUDE_ZGC
+#if INCLUDE_SHENANDOAHGC
+  if (UseShenandoahGC) {
+    ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
+    SET_ADDRESS(_C1_blobs, bs->pre_barrier_c1_runtime_code_blob()->code_begin());
+    SET_ADDRESS(_C1_blobs, bs->load_reference_barrier_strong_rt_code_blob()->code_begin());
+    SET_ADDRESS(_C1_blobs, bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin());
+    SET_ADDRESS(_C1_blobs, bs->load_reference_barrier_weak_rt_code_blob()->code_begin());
+    SET_ADDRESS(_C1_blobs, bs->load_reference_barrier_phantom_rt_code_blob()->code_begin());
+  }
+#endif // INCLUDE_SHENANDOAHGC
+#endif // COMPILER1
+
+  assert(_C1_blobs_length <= _C1_blobs_max, "increase _C1_blobs_max to %d", _C1_blobs_length);
+  _final_blobs_length = MAX2(_final_blobs_length, (_shared_blobs_max + _C2_blobs_max + _C1_blobs_length));
+  _c1_complete = true;
+  log_info(scc,init)("Runtime1 Blobs recorded");
+}
+
+#undef SET_ADDRESS
+#undef _extrs_max
+#undef _stubs_max
+#undef _blobs_max
+#undef _shared_blobs_max
+#undef _C1_blobs_max
+#undef _C2_blobs_max
+
+SCAddressTable::~SCAddressTable() {
+  if (_extrs_addr != nullptr) {
+    FREE_C_HEAP_ARRAY(address, _extrs_addr);
+  }
+  if (_stubs_addr != nullptr) {
+    FREE_C_HEAP_ARRAY(address, _stubs_addr);
+  }
+  if (_blobs_addr != nullptr) {
+    FREE_C_HEAP_ARRAY(address, _blobs_addr);
+  }
+}
+
+#define MAX_STR_COUNT 200
+static const char* _C_strings[MAX_STR_COUNT] = {nullptr};
+static int _C_strings_count = 0;
+static int _C_strings_s[MAX_STR_COUNT] = {0};
+static int _C_strings_id[MAX_STR_COUNT] = {0};
+static int _C_strings_len[MAX_STR_COUNT] = {0};
+static int _C_strings_hash[MAX_STR_COUNT] = {0};
+static int _C_strings_used = 0;
+
+void SCCache::load_strings() {
+  uint strings_count  = _load_header->strings_count();
+  if (strings_count == 0) {
+    return;
+  }
+  uint strings_offset = _load_header->strings_offset();
+  uint strings_size   = _load_header->entries_offset() - strings_offset;
+  uint data_size = (uint)(strings_count * sizeof(uint));
+  uint* sizes = (uint*)addr(strings_offset);
+  uint* hashs = (uint*)addr(strings_offset + data_size);
+  strings_size -= 2 * data_size;
+  // We have to keep cached strings longer than _cache buffer
+  // because they are refernced from compiled code which may
+  // still be executed on VM exit after _cache is freed.
+  char* p = NEW_C_HEAP_ARRAY(char, strings_size+1, mtCode);
+  memcpy(p, addr(strings_offset + 2 * data_size), strings_size);
+  _C_strings_buf = p;
+  assert(strings_count <= MAX_STR_COUNT, "sanity");
+  for (uint i = 0; i < strings_count; i++) {
+    _C_strings[i] = p;
+    uint len = sizes[i];
+    _C_strings_s[i] = i;
+    _C_strings_id[i] = i;
+    _C_strings_len[i] = len;
+    _C_strings_hash[i] = hashs[i];
+    p += len;
+  }
+  assert((uint)(p - _C_strings_buf) <= strings_size, "(" INTPTR_FORMAT " - " INTPTR_FORMAT ") = %d > %d ", p2i(p), p2i(_C_strings_buf), (uint)(p - _C_strings_buf), strings_size);
+  _C_strings_count = strings_count;
+  _C_strings_used  = strings_count;
+  log_info(scc, init)("Load %d C strings at offset %d from Startup Code Cache '%s'", _C_strings_count, strings_offset, _cache_path);
+}
+
+int SCCache::store_strings() {
+  uint offset = _write_position;
+  uint length = 0;
+  if (_C_strings_used > 0) {
+    // Write sizes first
+    for (int i = 0; i < _C_strings_used; i++) {
+      uint len = _C_strings_len[i] + 1; // Include 0
+      length += len;
+      assert(len < 1000, "big string: %s", _C_strings[i]);
+      uint n = write_bytes(&len, sizeof(uint));
+      if (n != sizeof(uint)) {
+        return -1;
+      }
+    }
+    // Write hashs
+    for (int i = 0; i < _C_strings_used; i++) {
+      uint n = write_bytes(&(_C_strings_hash[i]), sizeof(uint));
+      if (n != sizeof(uint)) {
+        return -1;
+      }
+    }
+    for (int i = 0; i < _C_strings_used; i++) {
+      uint len = _C_strings_len[i] + 1; // Include 0
+      uint n = write_bytes(_C_strings[_C_strings_s[i]], len);
+      if (n != len) {
+        return -1;
+      }
+    }
+    log_info(scc, exit)("Wrote %d C strings of total length %d at offset %d to Startup Code Cache '%s'",
+                        _C_strings_used, length, offset, _cache_path);
+  }
+  return _C_strings_used;
+}
+
+void SCCache::add_new_C_string(const char* str) {
+  assert(for_write(), "only when storing code");
+  _table->add_C_string(str);
+}
+
+void SCAddressTable::add_C_string(const char* str) {
+  if (str != nullptr && _complete && (_opto_complete || _c1_complete)) {
+    // Check previous strings address
+    for (int i = 0; i < _C_strings_count; i++) {
+      if (_C_strings[i] == str) {
+        return; // Found existing one
+      }
+    }
+    // Add new one
+    if (_C_strings_count < MAX_STR_COUNT) {
+      log_trace(scc)("add_C_string: [%d] " INTPTR_FORMAT " %s", _C_strings_count, p2i(str), str);
+      _C_strings_id[_C_strings_count] = -1; // Init
+      _C_strings[_C_strings_count++] = str;
+    } else {
+      if (Thread::current()->is_Compiler_thread()) {
+        CompileTask* task = ciEnv::current()->task();
+        log_info(scc)("%d (L%d): Number of C strings > max %d %s",
+                      task->compile_id(), task->comp_level(), MAX_STR_COUNT, str);
+      }
+    }
+  }
+}
+
+int SCAddressTable::id_for_C_string(address str) {
+  for (int i = 0; i < _C_strings_count; i++) {
+    if (_C_strings[i] == (const char*)str) { // found
+      int id = _C_strings_id[i];
+      if (id >= 0) {
+        assert(id < _C_strings_used, "%d >= %d", id , _C_strings_used);
+        return id; // Found recorded
+      }
+      // Search for the same string content
+      int len = (int)strlen((const char*)str);
+      int hash = java_lang_String::hash_code((const jbyte*)str, len);
+      for (int j = 0; j < _C_strings_used; j++) {
+        if ((_C_strings_len[j] == len) && (_C_strings_hash[j] == hash)) {
+          _C_strings_id[i] = j; // Found match
+          return j;
+        }
+      }
+      // Not found in recorded, add new
+      id = _C_strings_used++;
+      _C_strings_s[id] = i;
+      _C_strings_id[i] = id;
+      _C_strings_len[id] = len;
+      _C_strings_hash[id] = hash;
+      return id;
+    }
+  }
+  return -1;
+}
+
+address SCAddressTable::address_for_C_string(int idx) {
+  assert(idx < _C_strings_count, "sanity");
+  return (address)_C_strings[idx];
+}
+
+int search_address(address addr, address* table, uint length) {
+  for (int i = 0; i < (int)length; i++) {
+    if (table[i] == addr) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+address SCAddressTable::address_for_id(int idx) {
+  if (!_complete) {
+    fatal("SCA table is not complete");
+  }
+  if (idx == -1) {
+    return (address)-1;
+  }
+  uint id = (uint)idx;
+  if (id >= _all_max && idx < (_all_max + _C_strings_count)) {
+    return address_for_C_string(idx - _all_max);
+  }
+  if (idx < 0 || id == (_extrs_length + _stubs_length + _final_blobs_length)) {
+    fatal("Incorrect id %d for SCA table", id);
+  }
+  if (idx > (_all_max + _C_strings_count)) {
+    return (address)os::init + idx;
+  }
+  if (id < _extrs_length) {
+    return _extrs_addr[id];
+  }
+  id -= _extrs_length;
+  if (id < _stubs_length) {
+    return _stubs_addr[id];
+  }
+  id -= _stubs_length;
+  if (id < _final_blobs_length) {
+    return _blobs_addr[id];
+  }
+  return nullptr;
+}
+
+int SCAddressTable::id_for_address(address addr, RelocIterator reloc, CodeBuffer* buffer) {
+  int id = -1;
+  if (addr == (address)-1) { // Static call stub has jump to itself
+    return id;
+  }
+  if (!_complete) {
+    fatal("SCA table is not complete");
+  }
+  // Seach for C string
+  id = id_for_C_string(addr);
+  if (id >=0) {
+    return id + _all_max;
+  }
+  if (StubRoutines::contains(addr)) {
+    // Search in stubs
+    id = search_address(addr, _stubs_addr, _stubs_length);
+    if (id < 0) {
+      StubCodeDesc* desc = StubCodeDesc::desc_for(addr);
+      if (desc == nullptr) {
+        desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset);
+      }
+      const char* sub_name = (desc != nullptr) ? desc->name() : "<unknown>";
+      fatal("Address " INTPTR_FORMAT " for Stub:%s is missing in SCA table", p2i(addr), sub_name);
+    } else {
+      id += _extrs_length;
+    }
+  } else {
+    CodeBlob* cb = CodeCache::find_blob(addr);
+    if (cb != nullptr) {
+      // Search in code blobs
+      id = search_address(addr, _blobs_addr, _final_blobs_length);
+      if (id < 0) {
+        fatal("Address " INTPTR_FORMAT " for Blob:%s is missing in SCA table", p2i(addr), cb->name());
+      } else {
+        id += _extrs_length + _stubs_length;
+      }
+    } else {
+      // Search in runtime functions
+      id = search_address(addr, _extrs_addr, _extrs_length);
+      if (id < 0) {
+        ResourceMark rm;
+        const int buflen = 1024;
+        char* func_name = NEW_RESOURCE_ARRAY(char, buflen);
+        int offset = 0;
+        if (os::dll_address_to_function_name(addr, func_name, buflen, &offset)) {
+          if (offset > 0) {
+            // Could be address of C string
+            uint dist = (uint)pointer_delta(addr, (address)os::init, 1);
+            CompileTask* task = ciEnv::current()->task();
+            uint compile_id = 0;
+            uint comp_level =0;
+            if (task != nullptr) { // this could be called from compiler runtime initialization (compiler blobs)
+              compile_id = task->compile_id();
+              comp_level = task->comp_level();
+            }
+            log_info(scc)("%d (L%d): Address " INTPTR_FORMAT " (offset %d) for runtime target '%s' is missing in SCA table",
+                          compile_id, comp_level, p2i(addr), dist, (const char*)addr);
+            assert(dist > (uint)(_all_max + MAX_STR_COUNT), "change encoding of distance");
+            return dist;
+          }
+          fatal("Address " INTPTR_FORMAT " for runtime target '%s+%d' is missing in SCA table", p2i(addr), func_name, offset);
+        } else {
+          os::print_location(tty, p2i(addr), true);
+          reloc.print_current_on(tty);
+#ifndef PRODUCT
+          buffer->print_on(tty);
+          buffer->decode();
+#endif // !PRODUCT
+          fatal("Address " INTPTR_FORMAT " for <unknown> is missing in SCA table", p2i(addr));
+        }
+      }
+    }
+  }
+  return id;
+}
+
+void AOTRuntimeConstants::initialize_from_runtime() {
+  BarrierSet* bs = BarrierSet::barrier_set();
+  if (bs->is_a(BarrierSet::CardTableBarrierSet)) {
+    CardTableBarrierSet* ctbs = ((CardTableBarrierSet*)bs);
+    _aot_runtime_constants._grain_shift = ctbs->grain_shift();
+    _aot_runtime_constants._card_shift = ctbs->card_shift();
+  }
+}
+
+AOTRuntimeConstants AOTRuntimeConstants::_aot_runtime_constants;
+
+address AOTRuntimeConstants::_field_addresses_list[] = {
+  grain_shift_address(),
+  card_shift_address(),
+  nullptr
+};
+
+
+void SCCache::wait_for_no_nmethod_readers() {
+  while (true) {
+    int cur = Atomic::load(&_nmethod_readers);
+    int upd = -(cur + 1);
+    if (cur >= 0 && Atomic::cmpxchg(&_nmethod_readers, cur, upd) == cur) {
+      // Success, no new readers should appear.
+      break;
+    }
+  }
+
+  // Now wait for all readers to leave.
+  SpinYield w;
+  while (Atomic::load(&_nmethod_readers) != -1) {
+    w.wait();
+  }
+}
+
+SCCache::ReadingMark::ReadingMark() {
+  while (true) {
+    int cur = Atomic::load(&_nmethod_readers);
+    if (cur < 0) {
+      // Cache is already closed, cannot proceed.
+      _failed = true;
+      return;
+    }
+    if (Atomic::cmpxchg(&_nmethod_readers, cur, cur + 1) == cur) {
+      // Successfully recorded ourselves as entered.
+      _failed = false;
+      return;
+    }
+  }
+}
+
+SCCache::ReadingMark::~ReadingMark() {
+  if (_failed) {
+    return;
+  }
+  while (true) {
+    int cur = Atomic::load(&_nmethod_readers);
+    if (cur > 0) {
+      // Cache is open, we are counting down towards 0.
+      if (Atomic::cmpxchg(&_nmethod_readers, cur, cur - 1) == cur) {
+        return;
+      }
+    } else {
+      // Cache is closed, we are counting up towards -1.
+      if (Atomic::cmpxchg(&_nmethod_readers, cur, cur + 1) == cur) {
+        return;
+      }
+    }
+  }
+}
diff a/src/hotspot/share/code/SCCache.hpp b/src/hotspot/share/code/SCCache.hpp
--- /dev/null
+++ b/src/hotspot/share/code/SCCache.hpp
@@ -0,0 +1,640 @@
+/*
+ * Copyright (c) 2023, 2024, 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.
+ *
+ */
+
+#ifndef SHARE_CODE_SCCACHE_HPP
+#define SHARE_CODE_SCCACHE_HPP
+
+/*
+ * Startup Code Cache (SCC) collects compiled code and metadata during
+ * an application training runs.
+ * In following "deployment" runs this code can me loaded into
+ * Code Cache as normal nmethods skipping JIT compilation.
+ * In additoin special compiled code is generated with class initialization
+ * barriers which can be called on first Java method invocation.
+ */
+ 
+class AbstractCompiler;
+class ciConstant;
+class ciEnv;
+class ciMethod;
+class CodeBuffer;
+class CodeOffsets;
+class CompileTask;
+class DebugInformationRecorder;
+class Dependencies;
+class ExceptionTable;
+class ExceptionHandlerTable;
+class ImplicitExceptionTable;
+class JavaThread;
+class methodHandle;
+class Method;
+class OopMapSet;
+class OopRecorder;
+class outputStream;
+class RelocIterator;
+class SCCache;
+class StubCodeGenerator;
+
+enum class vmIntrinsicID : int;
+enum CompLevel : signed char;
+
+class SCConfig {
+  uint _compressedOopShift;
+  uint _compressedKlassShift;
+  uint _contendedPaddingWidth;
+  uint _objectAlignment;
+  uint _gc;
+  enum Flags {
+    none                     = 0,
+    metadataPointers         = 1,
+    debugVM                  = 2,
+    compressedOops           = 4,
+    compressedClassPointers  = 8,
+    useTLAB                  = 16,
+    systemClassAssertions    = 32,
+    userClassAssertions      = 64,
+    enableContendedPadding   = 128,
+    restrictContendedPadding = 256,
+  };
+  uint _flags;
+
+public:
+  void record(bool use_meta_ptrs);
+  bool verify(const char* cache_path) const;
+
+  bool has_meta_ptrs()  const { return (_flags & metadataPointers) != 0; }
+};
+
+// Code Cache file header
+class SCCHeader : public CHeapObj<mtCode> {
+private:
+  // Here should be version and other verification fields
+  enum {
+    SCC_VERSION = 1
+  };
+  uint _version;           // SCC version (should match when reading code cache)
+  uint _jvm_version_offset;// JVM version string
+  uint _cache_size;        // cache size in bytes
+  uint _strings_count;
+  uint _strings_offset;    // offset to recorded C strings
+  uint _entries_count;     // number of recorded entries in cache
+  uint _entries_offset;    // offset of SCCEntry array describing entries
+  uint _preload_entries_count; // entries for pre-loading code
+  uint _preload_entries_offset;
+  SCConfig _config;
+
+public:
+  void init(uint jvm_version_offset, uint cache_size,
+            uint strings_count, uint strings_offset,
+            uint entries_count, uint entries_offset,
+            uint preload_entries_count, uint preload_entries_offset,
+            bool use_meta_ptrs) {
+    _version        = SCC_VERSION;
+     _jvm_version_offset = jvm_version_offset;
+    _cache_size     = cache_size;
+    _strings_count  = strings_count;
+    _strings_offset = strings_offset;
+    _entries_count  = entries_count;
+    _entries_offset = entries_offset;
+    _preload_entries_count  = preload_entries_count;
+    _preload_entries_offset = preload_entries_offset;
+
+    _config.record(use_meta_ptrs);
+  }
+
+  uint jvm_version_offset() const { return _jvm_version_offset; }
+
+  uint cache_size()     const { return _cache_size; }
+  uint strings_count()  const { return _strings_count; }
+  uint strings_offset() const { return _strings_offset; }
+  uint entries_count()  const { return _entries_count; }
+  uint entries_offset() const { return _entries_offset; }
+  uint preload_entries_count()  const { return _preload_entries_count; }
+  uint preload_entries_offset() const { return _preload_entries_offset; }
+  bool has_meta_ptrs()  const { return _config.has_meta_ptrs(); }
+
+  bool verify_config(const char* cache_path, uint load_size)  const;
+  bool verify_vm_config(const char* cache_path) const { // Called after Universe initialized
+    return _config.verify(cache_path);
+  }
+};
+
+// Code Cache's entry contain information from CodeBuffer
+class SCCEntry {
+public:
+  enum Kind {
+    None = 0,
+    Stub = 1,
+    Blob = 2,
+    Code = 3
+  };
+
+private:
+  SCCEntry* _next;
+  Method*   _method;
+  Kind   _kind;        //
+  uint   _id;          // vmIntrinsic::ID for stub or name's hash for nmethod
+
+  uint   _offset;      // Offset to entry
+  uint   _size;        // Entry size
+  uint   _name_offset; // Method's or intrinsic name
+  uint   _name_size;
+  uint   _code_offset; // Start of code in cache
+  uint   _code_size;   // Total size of all code sections
+  uint   _reloc_offset;// Relocations
+  uint   _reloc_size;  // Max size of relocations per code section
+  uint   _num_inlined_bytecodes;
+
+  uint   _comp_level;  // compilation level
+  uint   _comp_id;     // compilation id
+  uint   _decompile;   // Decompile count for this nmethod
+  bool   _has_clinit_barriers; // Generated code has class init checks
+  bool   _for_preload; // Code can be used for preload
+  bool   _loaded;      // Code was loaded
+  bool   _not_entrant; // Deoptimized
+  bool   _load_fail;   // Failed to load due to some klass state
+  bool   _ignore_decompile; // ignore decompile counter if compilation is done
+                            // during "assembly" phase without running application
+
+public:
+  SCCEntry(uint offset, uint size, uint name_offset, uint name_size,
+           uint code_offset, uint code_size,
+           uint reloc_offset, uint reloc_size,
+           Kind kind, uint id, uint comp_level = 0,
+           uint comp_id = 0, uint decomp = 0,
+           bool has_clinit_barriers = false,
+           bool for_preload = false,
+           bool ignore_decompile = false) {
+    _next         = nullptr;
+    _method       = nullptr;
+    _kind         = kind;
+    _id           = id;
+
+    _offset       = offset;
+    _size         = size;
+    _name_offset  = name_offset;
+    _name_size    = name_size;
+    _code_offset  = code_offset;
+    _code_size    = code_size;
+    _reloc_offset = reloc_offset;
+    _reloc_size   = reloc_size;
+    _num_inlined_bytecodes = 0;
+
+    _comp_level   = comp_level;
+    _comp_id      = comp_id;
+    _decompile    = decomp;
+    _has_clinit_barriers = has_clinit_barriers;
+    _for_preload  = for_preload;
+    _loaded       = false;
+    _not_entrant  = false;
+    _load_fail    = false;
+    _ignore_decompile = ignore_decompile;
+  }
+  void* operator new(size_t x, SCCache* cache);
+  // Delete is a NOP
+  void operator delete( void *ptr ) {}
+
+  SCCEntry* next()    const { return _next; }
+  void set_next(SCCEntry* next) { _next = next; }
+
+  Method*   method()  const { return _method; }
+  void set_method(Method* method) { _method = method; }
+  void update_method_for_writing();
+
+  Kind kind()         const { return _kind; }
+  uint id()           const { return _id; }
+
+  uint offset()       const { return _offset; }
+  void set_offset(uint off) { _offset = off; }
+
+  uint size()         const { return _size; }
+  uint name_offset()  const { return _name_offset; }
+  uint name_size()    const { return _name_size; }
+  uint code_offset()  const { return _code_offset; }
+  uint code_size()    const { return _code_size; }
+  uint reloc_offset() const { return _reloc_offset; }
+  uint reloc_size()   const { return _reloc_size; }
+  uint num_inlined_bytecodes() const { return _num_inlined_bytecodes; }
+  void set_inlined_bytecodes(int bytes) { _num_inlined_bytecodes = bytes; }
+
+  uint comp_level()   const { return _comp_level; }
+  uint comp_id()      const { return _comp_id; }
+
+  uint decompile()    const { return _decompile; }
+  bool has_clinit_barriers() const { return _has_clinit_barriers; }
+  bool for_preload()  const { return _for_preload; }
+  bool is_loaded()    const { return _loaded; }
+  void set_loaded()         { _loaded = true; }
+  bool ignore_decompile() const { return _ignore_decompile; }
+
+  bool not_entrant()  const { return _not_entrant; }
+  void set_not_entrant()    { _not_entrant = true; }
+  void set_entrant()        { _not_entrant = false; }
+
+  bool load_fail()  const { return _load_fail; }
+  void set_load_fail()    { _load_fail = true; }
+
+  void print(outputStream* st) const;
+};
+
+// Addresses of stubs, blobs and runtime finctions called from compiled code.
+class SCAddressTable : public CHeapObj<mtCode> {
+private:
+  address* _extrs_addr;
+  address* _stubs_addr;
+  address* _blobs_addr;
+  address* _C1_blobs_addr;
+  address* _C2_blobs_addr;
+  uint     _extrs_length;
+  uint     _stubs_length;
+  uint     _blobs_length;
+  uint     _C1_blobs_length;
+  uint     _C2_blobs_length;
+  uint     _final_blobs_length;
+
+  bool _complete;
+  bool _opto_complete;
+  bool _c1_complete;
+
+public:
+  SCAddressTable() {
+    _extrs_addr = nullptr;
+    _stubs_addr = nullptr;
+    _blobs_addr = nullptr;
+    _complete = false;
+    _opto_complete = false;
+    _c1_complete = false;
+  }
+  ~SCAddressTable();
+  void init();
+  void init_opto();
+  void init_c1();
+  void add_C_string(const char* str);
+  int  id_for_C_string(address str);
+  address address_for_C_string(int idx);
+  int  id_for_address(address addr, RelocIterator iter, CodeBuffer* buffer);
+  address address_for_id(int id);
+  bool opto_complete() const { return _opto_complete; }
+  bool c1_complete() const { return _c1_complete; }
+};
+
+struct SCCodeSection {
+public:
+  address _origin_address;
+  uint _size;
+  uint _offset;
+};
+
+enum class DataKind: int {
+  No_Data   = -1,
+  Null      = 0,
+  Klass     = 1,
+  Method    = 2,
+  String    = 3,
+  Primitive = 4, // primitive Class object
+  SysLoader = 5, // java_system_loader
+  PlaLoader = 6, // java_platform_loader
+  MethodCnts= 7,
+  Klass_Shared  = 8,
+  Method_Shared = 9,
+  String_Shared = 10,
+  MH_Oop_Shared = 11
+};
+
+class SCCache;
+
+class SCCReader { // Concurent per compilation request
+private:
+  const SCCache*  _cache;
+  const SCCEntry* _entry;
+  const char*     _load_buffer; // Loaded cached code buffer
+  uint  _read_position;            // Position in _load_buffer
+  uint  read_position() const { return _read_position; }
+  void  set_read_position(uint pos);
+  const char* addr(uint offset) const { return _load_buffer + offset; }
+
+  uint _compile_id;
+  uint _comp_level;
+  uint compile_id() const { return _compile_id; }
+  uint comp_level() const { return _comp_level; }
+
+  bool _preload;             // Preloading code before method execution
+  bool _lookup_failed;       // Failed to lookup for info (skip only this code load)
+  void set_lookup_failed()     { _lookup_failed = true; }
+  void clear_lookup_failed()   { _lookup_failed = false; }
+  bool lookup_failed()   const { return _lookup_failed; }
+
+public:
+  SCCReader(SCCache* cache, SCCEntry* entry, CompileTask* task);
+
+  bool compile(ciEnv* env, ciMethod* target, int entry_bci, AbstractCompiler* compiler);
+  bool compile_blob(CodeBuffer* buffer, int* pc_offset);
+
+  Klass* read_klass(const methodHandle& comp_method, bool shared);
+  Method* read_method(const methodHandle& comp_method, bool shared);
+
+  bool read_code(CodeBuffer* buffer, CodeBuffer* orig_buffer, uint code_offset);
+  bool read_relocations(CodeBuffer* buffer, CodeBuffer* orig_buffer, OopRecorder* oop_recorder, ciMethod* target);
+  DebugInformationRecorder* read_debug_info(OopRecorder* oop_recorder);
+  OopMapSet* read_oop_maps();
+  bool read_dependencies(Dependencies* dependencies);
+
+  jobject read_oop(JavaThread* thread, const methodHandle& comp_method);
+  Metadata* read_metadata(const methodHandle& comp_method);
+  bool read_oops(OopRecorder* oop_recorder, ciMethod* target);
+  bool read_metadata(OopRecorder* oop_recorder, ciMethod* target);
+
+  void print_on(outputStream* st);
+};
+
+class SCCache : public CHeapObj<mtCode> {
+private:
+  SCCHeader*  _load_header;
+  const char* _cache_path;
+  char*       _load_buffer;    // Aligned buffer for loading cached code
+  char*       _store_buffer;   // Aligned buffer for storing cached code
+  char*       _C_load_buffer;  // Original unaligned buffer
+  char*       _C_store_buffer; // Original unaligned buffer
+
+  uint        _write_position; // Position in _store_buffer
+  uint        _load_size;      // Used when reading cache
+  uint        _store_size;     // Used when writing cache
+  bool _for_read;              // Open for read
+  bool _for_write;             // Open for write
+  bool _use_meta_ptrs;         // Store metadata pointers
+  bool _for_preload;           // Code for preload
+  bool _gen_preload_code;      // Generate pre-loading code
+  bool _has_clinit_barriers;   // Code with clinit barriers
+  bool _closing;               // Closing cache file
+  bool _failed;                // Failed read/write to/from cache (cache is broken?)
+
+  SCAddressTable* _table;
+
+  SCCEntry* _load_entries;     // Used when reading cache
+  uint*     _search_entries;   // sorted by ID table [id, index]
+  SCCEntry* _store_entries;    // Used when writing cache
+  const char* _C_strings_buf;  // Loaded buffer for _C_strings[] table
+  uint      _store_entries_cnt;
+
+  uint _compile_id;
+  uint _comp_level;
+  uint compile_id() const { return _compile_id; }
+  uint comp_level() const { return _comp_level; }
+
+  static SCCache* open_for_read();
+  static SCCache* open_for_write();
+
+  bool set_write_position(uint pos);
+  bool align_write();
+  uint write_bytes(const void* buffer, uint nbytes);
+  const char* addr(uint offset) const { return _load_buffer + offset; }
+
+  bool _lookup_failed;       // Failed to lookup for info (skip only this code load)
+  void set_lookup_failed()     { _lookup_failed = true; }
+  void clear_lookup_failed()   { _lookup_failed = false; }
+  bool lookup_failed()   const { return _lookup_failed; }
+
+  SCCEntry* write_nmethod(const methodHandle& method,
+                          int compile_id,
+                          int entry_bci,
+                          CodeOffsets* offsets,
+                          int orig_pc_offset,
+                          DebugInformationRecorder* recorder,
+                          Dependencies* dependencies,
+                          CodeBuffer *code_buffer,
+                          int frame_size,
+                          OopMapSet* oop_maps,
+                          ExceptionHandlerTable* handler_table,
+                          ImplicitExceptionTable* nul_chk_table,
+                          AbstractCompiler* compiler,
+                          CompLevel comp_level,
+                          bool has_clinit_barriers,
+                          bool for_preload,
+                          bool has_unsafe_access,
+                          bool has_wide_vectors,
+                          bool has_monitors,
+                          bool has_scoped_access);
+
+  // States:
+  //   S >= 0: allow new readers, S readers are currently active
+  //   S <  0: no new readers are allowed; (-S-1) readers are currently active
+  //     (special case: S = -1 means no readers are active, and would never be active again)
+  static volatile int _nmethod_readers;
+
+  static void wait_for_no_nmethod_readers();
+
+  class ReadingMark {
+  private:
+    bool _failed;
+  public:
+    ReadingMark();
+    ~ReadingMark();
+    bool failed() {
+      return _failed;
+    }
+  };
+
+public:
+  SCCache(const char* cache_path, int fd, uint load_size);
+  ~SCCache();
+
+  const char* cache_buffer() const { return _load_buffer; }
+  const char* cache_path()   const { return _cache_path; }
+  bool failed() const { return _failed; }
+  void set_failed()   { _failed = true; }
+
+  uint load_size() const { return _load_size; }
+  uint write_position() const { return _write_position; }
+
+  void load_strings();
+  int store_strings();
+
+  static void init_table();
+  static void init_opto_table();
+  static void init_c1_table();
+  address address_for_id(int id) const { return _table->address_for_id(id); }
+
+  bool for_read()  const { return _for_read  && !_failed; }
+  bool for_write() const { return _for_write && !_failed; }
+
+  bool closing()          const { return _closing; }
+  bool use_meta_ptrs()    const { return _use_meta_ptrs; }
+  bool gen_preload_code() const { return _gen_preload_code; }
+
+  void add_new_C_string(const char* str);
+
+  SCCEntry* add_entry() {
+    _store_entries_cnt++;
+    _store_entries -= 1;
+    return _store_entries;
+  }
+  void preload_startup_code(TRAPS);
+
+  SCCEntry* find_entry(SCCEntry::Kind kind, uint id, uint comp_level = 0, uint decomp = 0);
+  void invalidate_entry(SCCEntry* entry);
+
+  bool finish_write();
+
+  static bool load_stub(StubCodeGenerator* cgen, vmIntrinsicID id, const char* name, address start);
+  static bool store_stub(StubCodeGenerator* cgen, vmIntrinsicID id, const char* name, address start);
+
+  bool write_klass(Klass* klass);
+  bool write_method(Method* method);
+
+  bool write_code(CodeBuffer* buffer, uint& code_size);
+  bool write_relocations(CodeBuffer* buffer, uint& reloc_size);
+  bool write_debug_info(DebugInformationRecorder* recorder);
+  bool write_oop_maps(OopMapSet* oop_maps);
+
+  jobject read_oop(JavaThread* thread, const methodHandle& comp_method);
+  Metadata* read_metadata(const methodHandle& comp_method);
+  bool read_oops(OopRecorder* oop_recorder, ciMethod* target);
+  bool read_metadata(OopRecorder* oop_recorder, ciMethod* target);
+
+  bool write_oop(jobject& jo);
+  bool write_oops(OopRecorder* oop_recorder);
+  bool write_metadata(Metadata* m);
+  bool write_metadata(OopRecorder* oop_recorder);
+
+  static bool load_exception_blob(CodeBuffer* buffer, int* pc_offset);
+  static bool store_exception_blob(CodeBuffer* buffer, int pc_offset);
+
+  static bool load_nmethod(ciEnv* env, ciMethod* target, int entry_bci, AbstractCompiler* compiler, CompLevel comp_level);
+
+  static SCCEntry* store_nmethod(const methodHandle& method,
+                     int compile_id,
+                     int entry_bci,
+                     CodeOffsets* offsets,
+                     int orig_pc_offset,
+                     DebugInformationRecorder* recorder,
+                     Dependencies* dependencies,
+                     CodeBuffer *code_buffer,
+                     int frame_size,
+                     OopMapSet* oop_maps,
+                     ExceptionHandlerTable* handler_table,
+                     ImplicitExceptionTable* nul_chk_table,
+                     AbstractCompiler* compiler,
+                     CompLevel comp_level,
+                     bool has_clinit_barriers,
+                     bool for_preload,
+                     bool has_unsafe_access,
+                     bool has_wide_vectors,
+                     bool has_monitors,
+                     bool has_scoped_access);
+
+  static uint store_entries_cnt() {
+    if (is_on_for_write()) {
+      return cache()->_store_entries_cnt;
+    }
+    return -1;
+  }
+
+// Static access
+
+private:
+  static SCCache*  _cache;
+
+  static bool open_cache(const char* cache_path);
+  static bool verify_vm_config() {
+    if (is_on_for_read()) {
+      return _cache->_load_header->verify_vm_config(_cache->_cache_path);
+    }
+    return true;
+  }
+public:
+  static SCCache* cache() { return _cache; }
+  static void initialize();
+  static void init2();
+  static void close();
+  static bool is_on() { return _cache != nullptr && !_cache->closing(); }
+  static bool is_C3_on();
+  static bool is_code_load_thread_on();
+  static bool is_on_for_read()  { return is_on() && _cache->for_read(); }
+  static bool is_on_for_write() { return is_on() && _cache->for_write(); }
+  static bool gen_preload_code(ciMethod* m, int entry_bci);
+  static bool allow_const_field(ciConstant& value);
+  static void invalidate(SCCEntry* entry);
+  static bool is_loaded(SCCEntry* entry);
+  static SCCEntry* find_code_entry(const methodHandle& method, uint comp_level);
+  static void preload_code(JavaThread* thread);
+
+  template<typename Function>
+  static void iterate(Function function) { // lambda enabled API
+    SCCache* cache = open_for_read();
+    if (cache != nullptr) {
+      ReadingMark rdmk;
+      if (rdmk.failed()) {
+        // Cache is closed, cannot touch anything.
+        return;
+      }
+
+      uint count = cache->_load_header->entries_count();
+      uint* search_entries = (uint*)cache->addr(cache->_load_header->entries_offset()); // [id, index]
+      SCCEntry* load_entries = (SCCEntry*)(search_entries + 2 * count);
+
+      for (uint i = 0; i < count; i++) {
+        int index = search_entries[2*i + 1];
+        SCCEntry* entry = &(load_entries[index]);
+        function(entry);
+      }
+    }
+  }
+
+  static void add_C_string(const char* str);
+
+  static void print_on(outputStream* st);
+  static void print_statistics_on(outputStream* st);
+  static void print_timers_on(outputStream* st);
+  static void print_unused_entries_on(outputStream* st);
+
+  static void new_workflow_start_writing_cache() NOT_CDS_JAVA_HEAP_RETURN;
+  static void new_workflow_end_writing_cache() NOT_CDS_JAVA_HEAP_RETURN;
+  static void new_workflow_load_cache() NOT_CDS_JAVA_HEAP_RETURN;
+};
+
+// code cache internal runtime constants area used by AOT code
+class AOTRuntimeConstants {
+ friend class SCCache;
+  uint _grain_shift;
+  uint _card_shift;
+  static address _field_addresses_list[];
+  static AOTRuntimeConstants _aot_runtime_constants;
+  // private constructor for unique singleton
+  AOTRuntimeConstants() { }
+  // private for use by friend class SCCache
+  static void initialize_from_runtime();
+ public:
+  static bool contains(address adr) {
+    address base = (address)&_aot_runtime_constants;
+    address hi = base + sizeof(AOTRuntimeConstants);
+    return (base <= adr && adr < hi);
+  }
+  static address grain_shift_address() { return (address)&_aot_runtime_constants._grain_shift; }
+  static address card_shift_address() { return (address)&_aot_runtime_constants._card_shift; }
+  static address* field_addresses_list() {
+    return _field_addresses_list;
+  }
+};
+
+#endif // SHARE_CODE_SCCACHE_HPP
diff a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -21,18 +21,20 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/cdsAccess.hpp"
 #include "code/codeBlob.hpp"
 #include "code/codeCache.hpp"
 #include "code/codeHeapState.hpp"
 #include "code/compiledIC.hpp"
 #include "code/dependencies.hpp"
 #include "code/dependencyContext.hpp"
 #include "code/nmethod.hpp"
 #include "code/pcDesc.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSetNMethod.hpp"
@@ -168,10 +170,12 @@
 address CodeCache::_low_bound = nullptr;
 address CodeCache::_high_bound = nullptr;
 volatile int CodeCache::_number_of_nmethods_with_dependencies = 0;
 ExceptionCache* volatile CodeCache::_exception_cache_purge_list = nullptr;
 
+static ReservedSpace _cds_code_space;
+
 // Initialize arrays of CodeHeap subsets
 GrowableArray<CodeHeap*>* CodeCache::_heaps = new(mtCode) GrowableArray<CodeHeap*> (static_cast<int>(CodeBlobType::All), mtCode);
 GrowableArray<CodeHeap*>* CodeCache::_nmethod_heaps = new(mtCode) GrowableArray<CodeHeap*> (static_cast<int>(CodeBlobType::All), mtCode);
 GrowableArray<CodeHeap*>* CodeCache::_allocable_heaps = new(mtCode) GrowableArray<CodeHeap*> (static_cast<int>(CodeBlobType::All), mtCode);
 
@@ -195,11 +199,10 @@
   assert(!heap->set, "sanity");
   heap->size = (available_size > (used_size + min_size)) ? (available_size - used_size) : min_size;
 }
 
 void CodeCache::initialize_heaps() {
-
   CodeHeapInfo non_nmethod = {NonNMethodCodeHeapSize, FLAG_IS_CMDLINE(NonNMethodCodeHeapSize), true};
   CodeHeapInfo profiled = {ProfiledCodeHeapSize, FLAG_IS_CMDLINE(ProfiledCodeHeapSize), true};
   CodeHeapInfo non_profiled = {NonProfiledCodeHeapSize, FLAG_IS_CMDLINE(NonProfiledCodeHeapSize), true};
 
   const bool cache_size_set   = FLAG_IS_CMDLINE(ReservedCodeCacheSize);
@@ -222,10 +225,11 @@
   assert(heap_available(CodeBlobType::MethodNonProfiled), "MethodNonProfiled heap is always available for segmented code heap");
 
   size_t compiler_buffer_size = 0;
   COMPILER1_PRESENT(compiler_buffer_size += CompilationPolicy::c1_count() * Compiler::code_buffer_size());
   COMPILER2_PRESENT(compiler_buffer_size += CompilationPolicy::c2_count() * C2Compiler::initial_code_buffer_size());
+  COMPILER2_PRESENT(compiler_buffer_size += (CompilationPolicy::c2_count() + CompilationPolicy::c3_count()) * C2Compiler::initial_code_buffer_size());
 
   if (!non_nmethod.set) {
     non_nmethod.size += compiler_buffer_size;
     // Further down, just before FLAG_SET_ERGO(), all segment sizes are
     // aligned down to the next lower multiple of min_size. For large page
@@ -316,16 +320,25 @@
   FLAG_SET_ERGO(NonNMethodCodeHeapSize, non_nmethod.size);
   FLAG_SET_ERGO(ProfiledCodeHeapSize, profiled.size);
   FLAG_SET_ERGO(NonProfiledCodeHeapSize, non_profiled.size);
   FLAG_SET_ERGO(ReservedCodeCacheSize, cache_size);
 
+  const size_t cds_code_size = align_up(CDSAccess::get_cached_code_size(), min_size);
+  cache_size += cds_code_size;
+
   ReservedCodeSpace rs = reserve_heap_memory(cache_size, ps);
 
   // Register CodeHeaps with LSan as we sometimes embed pointers to malloc memory.
   LSAN_REGISTER_ROOT_REGION(rs.base(), rs.size());
 
   size_t offset = 0;
+  if (cds_code_size > 0) {
+    // FIXME: use CodeHeapInfo for this hack ...
+    _cds_code_space = rs.partition(offset, cds_code_size);
+    offset += cds_code_size;
+  }
+
   if (profiled.enabled) {
     ReservedSpace profiled_space = rs.partition(offset, profiled.size);
     offset += profiled.size;
     // Tier 2 and tier 3 (profiled) methods
     add_heap(profiled_space, "CodeHeap 'profiled nmethods'", CodeBlobType::MethodProfiled);
@@ -341,10 +354,18 @@
     // Tier 1 and tier 4 (non-profiled) methods and native methods
     add_heap(non_profiled_space, "CodeHeap 'non-profiled nmethods'", CodeBlobType::MethodNonProfiled);
   }
 }
 
+void* CodeCache::map_cached_code() {
+  if (_cds_code_space.size() > 0 && CDSAccess::map_cached_code(_cds_code_space)) {
+    return _cds_code_space.base();
+  } else {
+    return nullptr;
+  }
+}
+
 size_t CodeCache::page_size(bool aligned, size_t min_pages) {
   return aligned ? os::page_size_for_region_aligned(ReservedCodeCacheSize, min_pages) :
                    os::page_size_for_region_unaligned(ReservedCodeCacheSize, min_pages);
 }
 
@@ -1205,15 +1226,16 @@
         DependencySignature* current_sig = new DependencySignature(deps);
 
         // Determine if dependency is already checked. table->put(...) returns
         // 'true' if the dependency is added (i.e., was not in the hashtable).
         if (table->put(*current_sig, 1)) {
-          if (deps.check_dependency() != nullptr) {
+          Klass* witness = deps.check_dependency();
+          if (witness != nullptr) {
             // Dependency checking failed. Print out information about the failed
             // dependency and finally fail with an assert. We can fail here, since
             // dependency checking is never done in a product build.
-            tty->print_cr("Failed dependency:");
+            deps.print_dependency(tty, witness, true);
             changes.print();
             nm->print();
             nm->print_dependencies_on(tty);
             assert(false, "Should have been marked for deoptimization");
           }
@@ -1235,10 +1257,17 @@
   // nmethod::check_all_dependencies works only correctly, if no safepoint
   // can happen
   NoSafepointVerifier nsv;
   for (DepChange::ContextStream str(changes, nsv); str.next(); ) {
     InstanceKlass* d = str.klass();
+    {
+      LogStreamHandle(Trace, dependencies) log;
+      if (log.is_enabled()) {
+        log.print("Processing context ");
+        d->name()->print_value_on(&log);
+      }
+    }
     d->mark_dependent_nmethods(deopt_scope, changes);
   }
 
 #ifndef PRODUCT
   if (VerifyDependencies) {
@@ -1493,10 +1522,89 @@
   tty->print_cr("Allocated in freelist:          " SSIZE_FORMAT "kB",  bytes_allocated_in_freelists()/K);
   tty->print_cr("Unused bytes in CodeBlobs:      " SSIZE_FORMAT "kB",  (wasted_bytes/K));
   tty->print_cr("Segment map size:               " SSIZE_FORMAT "kB",  allocated_segments()/K); // 1 byte per segment
 }
 
+static void print_helper1(outputStream* st, const char* prefix, int total, int not_entrant, int used) {
+  if (total > 0) {
+    double ratio = (100.0 * used) / total;
+    st->print("%s %3d nmethods: %3d not_entrant, %d used (%2.1f%%)", prefix, total, not_entrant, used, ratio);
+  }
+}
+
+void CodeCache::print_nmethod_statistics_on(outputStream* st) {
+  int stats     [2][6][3][2] = {0};
+  int stats_used[2][6][3][2] = {0};
+
+  int total_osr = 0;
+  int total_entrant = 0;
+  int total_non_entrant = 0;
+  int total_other = 0;
+  int total_used = 0;
+
+  NMethodIterator iter(NMethodIterator::all);
+  while (iter.next()) {
+    nmethod* nm = iter.method();
+    if (nm->is_in_use()) {
+      ++total_entrant;
+    } else if (nm->is_not_entrant()) {
+      ++total_non_entrant;
+    } else {
+      ++total_other;
+    }
+    if (nm->is_osr_method()) {
+      ++total_osr;
+    }
+    if (nm->used()) {
+      ++total_used;
+    }
+    assert(!nm->preloaded() || nm->comp_level() == CompLevel_full_optimization, "");
+
+    int idx1 = nm->is_scc() ? 1 : 0;
+    int idx2 = nm->comp_level() + (nm->preloaded() ? 1 : 0);
+    int idx3 = (nm->is_in_use()      ? 0 :
+               (nm->is_not_entrant() ? 1 :
+                                       2));
+    int idx4 = (nm->is_osr_method() ? 1 : 0);
+    stats[idx1][idx2][idx3][idx4] += 1;
+    if (nm->used()) {
+      stats_used[idx1][idx2][idx3][idx4] += 1;
+    }
+  }
+
+  st->print("Total: %d methods (%d entrant / %d not_entrant; osr: %d ",
+               total_entrant + total_non_entrant + total_other,
+               total_entrant, total_non_entrant, total_osr);
+  if (total_other > 0) {
+    st->print("; %d other", total_other);
+  }
+  st->print_cr(")");
+
+  for (int i = CompLevel_simple; i <= CompLevel_full_optimization; i++) {
+    int total_normal = stats[0][i][0][0] + stats[0][i][1][0] + stats[0][i][2][0];
+    int total_osr    = stats[0][i][0][1] + stats[0][i][1][1] + stats[0][i][2][1];
+    if (total_normal + total_osr > 0) {
+      st->print("  Tier%d:", i);
+      print_helper1(st,      "", total_normal, stats[0][i][1][0], stats_used[0][i][0][0] + stats_used[0][i][1][0]);
+      print_helper1(st, "; osr:", total_osr,    stats[0][i][1][1], stats_used[0][i][0][1] + stats_used[0][i][1][1]);
+      st->cr();
+    }
+  }
+  st->cr();
+  for (int i = CompLevel_simple; i <= CompLevel_full_optimization + 1; i++) {
+    int total_normal = stats[1][i][0][0] + stats[1][i][1][0] + stats[1][i][2][0];
+    int total_osr    = stats[1][i][0][1] + stats[1][i][1][1] + stats[1][i][2][1];
+    assert(total_osr == 0, "sanity");
+    if (total_normal + total_osr > 0) {
+      st->print("  SC T%d:", i);
+      print_helper1(st,      "", total_normal, stats[1][i][1][0], stats_used[1][i][0][0] + stats_used[1][i][1][0]);
+      print_helper1(st, "; osr:", total_osr,    stats[1][i][1][1], stats_used[1][i][0][1] + stats_used[1][i][1][1]);
+      st->cr();
+    }
+  }
+}
+
 //------------------------------------------------------------------------------------------------
 // Non-product version
 
 #ifndef PRODUCT
 
@@ -1526,24 +1634,20 @@
   int max_nm_size = 0;
   ResourceMark rm;
 
   int i = 0;
   FOR_ALL_ALLOCABLE_HEAPS(heap) {
-    if ((_nmethod_heaps->length() >= 1) && Verbose) {
-      tty->print_cr("-- %s --", (*heap)->name());
-    }
+    int heap_total = 0;
+    tty->print_cr("-- %s --", (*heap)->name());
     FOR_ALL_BLOBS(cb, *heap) {
       total++;
+      heap_total++;
       if (cb->is_nmethod()) {
         nmethod* nm = (nmethod*)cb;
 
-        if (Verbose && nm->method() != nullptr) {
-          ResourceMark rm;
-          char *method_name = nm->method()->name_and_sig_as_C_string();
-          tty->print("%s", method_name);
-          if(nm->is_not_entrant()) { tty->print_cr(" not-entrant"); }
-        }
+        tty->print("%4d: ", heap_total);
+        CompileTask::print(tty, nm, (nm->is_not_entrant() ? "non-entrant" : ""), true, true);
 
         nmethodCount++;
 
         if(nm->is_not_entrant()) { nmethodNotEntrant++; }
         if(nm->method() != nullptr && nm->is_native_method()) { nmethodNative++; }
@@ -1701,10 +1805,29 @@
   }
 
 #endif // !PRODUCT
 }
 
+void CodeCache::print_nmethods_on(outputStream* st) {
+  ResourceMark rm;
+  int i = 0;
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
+    st->print_cr("-- %s --", (*heap)->name());
+    FOR_ALL_BLOBS(cb, *heap) {
+      i++;
+      if (cb->is_nmethod()) {
+        nmethod* nm = (nmethod*)cb;
+        st->print("%4d: ", i);
+        CompileTask::print(st, nm, nullptr, true, false);
+
+        const char non_entrant_char = (nm->is_not_entrant() ? 'N' : ' ');
+        st->print_cr(" %c", non_entrant_char);
+      }
+    }
+  }
+}
+
 void CodeCache::print_summary(outputStream* st, bool detailed) {
   int full_count = 0;
   julong total_used = 0;
   julong total_max_used = 0;
   julong total_free = 0;
diff a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp
--- a/src/hotspot/share/code/codeCache.hpp
+++ b/src/hotspot/share/code/codeCache.hpp
@@ -146,10 +146,11 @@
 
   static void add_heap(CodeHeap* heap);
   static const GrowableArray<CodeHeap*>* heaps() { return _heaps; }
   static const GrowableArray<CodeHeap*>* nmethod_heaps() { return _nmethod_heaps; }
 
+  static void* map_cached_code();
   // Allocation/administration
   static CodeBlob* allocate(uint size, CodeBlobType code_blob_type, bool handle_alloc_failure = true, CodeBlobType orig_code_blob_type = CodeBlobType::All); // allocates a new CodeBlob
   static void commit(CodeBlob* cb);                        // called when the allocated CodeBlob has been filled
   static void free(CodeBlob* cb);                          // frees a CodeBlob
   static void free_unused_tail(CodeBlob* cb, size_t used); // frees the unused tail of a CodeBlob (only used by TemplateInterpreter::initialize())
@@ -220,19 +221,22 @@
   static void purge_exception_caches();
 
   // Printing/debugging
   static void print();                           // prints summary
   static void print_internals();
+  static void print_nmethods_on(outputStream* st);
   static void print_memory_overhead();
   static void verify();                          // verifies the code cache
   static void print_trace(const char* event, CodeBlob* cb, uint size = 0) PRODUCT_RETURN;
   static void print_summary(outputStream* st, bool detailed = true); // Prints a summary of the code cache usage
   static void log_state(outputStream* st);
   LINUX_ONLY(static void write_perf_map(const char* filename, outputStream* st);) // Prints warnings and error messages to outputStream
   static const char* get_code_heap_name(CodeBlobType code_blob_type)  { return (heap_available(code_blob_type) ? get_code_heap(code_blob_type)->name() : "Unused"); }
   static void report_codemem_full(CodeBlobType code_blob_type, bool print);
 
+  static void print_nmethod_statistics_on(outputStream* st);
+
   // Dcmd (Diagnostic commands)
   static void print_codelist(outputStream* st);
   static void print_layout(outputStream* st);
 
   // The full limits of the codeCache
diff a/src/hotspot/share/code/debugInfoRec.cpp b/src/hotspot/share/code/debugInfoRec.cpp
--- a/src/hotspot/share/code/debugInfoRec.cpp
+++ b/src/hotspot/share/code/debugInfoRec.cpp
@@ -142,10 +142,32 @@
   add_new_pc_offset(PcDesc::lower_offset_limit);  // sentinel record
 
   debug_only(_recording_state = rs_null);
 }
 
+DebugInformationRecorder::DebugInformationRecorder(OopRecorder* oop_recorder, int data_size, int pcs_length)
+  : _recording_non_safepoints(compute_recording_non_safepoints())
+{
+  _pcs_size   = _pcs_length = pcs_length;
+  _pcs        = NEW_RESOURCE_ARRAY(PcDesc, _pcs_size);
+
+  _prev_safepoint_pc = PcDesc::lower_offset_limit;
+
+  _stream = new DebugInfoWriteStream(this, data_size);
+  // make sure that there is no stream_decode_offset that is zero
+  _stream->write_byte((jbyte)0xFF);
+
+  // make sure that we can distinguish the value "serialized_null" from offsets
+  assert(_stream->position() > serialized_null, "sanity");
+
+  _oop_recorder = oop_recorder;
+
+  _all_chunks = nullptr;
+  _next_chunk = _next_chunk_limit = nullptr;
+
+  debug_only(_recording_state = rs_null);
+}
 
 void DebugInformationRecorder::add_oopmap(int pc_offset, OopMap* map) {
   // !!!!! Preserve old style handling of oopmaps for now
   _oopmaps->add_gc_map(pc_offset, map);
 }
diff a/src/hotspot/share/code/debugInfoRec.hpp b/src/hotspot/share/code/debugInfoRec.hpp
--- a/src/hotspot/share/code/debugInfoRec.hpp
+++ b/src/hotspot/share/code/debugInfoRec.hpp
@@ -77,10 +77,11 @@
 
 class DebugInformationRecorder: public ResourceObj {
  public:
   // constructor
   DebugInformationRecorder(OopRecorder* oop_recorder);
+  DebugInformationRecorder(OopRecorder* oop_recorder, int data_size, int pcs_length);
 
   // adds an oopmap at a specific offset
   void add_oopmap(int pc_offset, OopMap* map);
 
   // adds a jvm mapping at pc-offset, for a safepoint only
diff a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp
--- a/src/hotspot/share/code/dependencies.cpp
+++ b/src/hotspot/share/code/dependencies.cpp
@@ -829,13 +829,17 @@
 
     st->cr();
   }
   if (witness != nullptr) {
     bool put_star = !Dependencies::is_concrete_klass(witness);
-    st->print_cr("  witness = %s%s",
-                  (put_star? "*": ""),
-                  witness->external_name());
+    st->print("  witness = %s%s",
+              (put_star ? "*": ""),
+              witness->external_name());
+    if (witness->is_instance_klass()) {
+      st->print(" (%s)", InstanceKlass::cast(witness)->init_state_name());
+    }
+    st->cr();
   }
 }
 
 void Dependencies::DepStream::log_dependency(Klass* witness) {
   if (_deps == nullptr && xtty == nullptr)  return;  // fast cutout for runtime
diff a/src/hotspot/share/code/dependencies.hpp b/src/hotspot/share/code/dependencies.hpp
--- a/src/hotspot/share/code/dependencies.hpp
+++ b/src/hotspot/share/code/dependencies.hpp
@@ -461,10 +461,16 @@
   size_t size_in_bytes() {
     assert(_content_bytes != nullptr, "encode it first");
     return _size_in_bytes;
   }
 
+  void set_content(address content_bytes, int size_in_bytes) {
+    assert(_content_bytes == nullptr, "not intialized expected");
+    _content_bytes = content_bytes;
+    _size_in_bytes = size_in_bytes;
+  }
+
   OopRecorder* oop_recorder() { return _oop_recorder; }
   CompileLog*  log()          { return _log; }
 
   void copy_to(nmethod* nm);
 
diff a/src/hotspot/share/code/dependencyContext.cpp b/src/hotspot/share/code/dependencyContext.cpp
--- a/src/hotspot/share/code/dependencyContext.cpp
+++ b/src/hotspot/share/code/dependencyContext.cpp
@@ -65,13 +65,25 @@
 //
 // Walk the list of dependent nmethods searching for nmethods which
 // are dependent on the changes that were passed in and mark them for
 // deoptimization.
 //
-void DependencyContext::mark_dependent_nmethods(DeoptimizationScope* deopt_scope, DepChange& changes) {
+void DependencyContext::mark_dependent_nmethods(DeoptimizationScope* deopt_scope, DepChange& changes, InstanceKlass* context) {
   for (nmethodBucket* b = dependencies_not_unloading(); b != nullptr; b = b->next_not_unloading()) {
     nmethod* nm = b->get_nmethod();
+    {
+      LogStreamHandle(Trace, dependencies) log;
+      if (log.is_enabled()) {
+        log.print("Processing ");
+        if (context != nullptr) {
+          log.print(" ctx=");
+          context->name()->print_value_on(&log);
+          log.print(" ");
+        }
+        nm->print_value_on(&log);
+      }
+    }
     if (nm->is_marked_for_deoptimization()) {
       deopt_scope->dependent(nm);
     } else if (nm->check_dependency_on(changes)) {
       LogTarget(Info, dependencies) lt;
       if (lt.is_enabled()) {
diff a/src/hotspot/share/code/dependencyContext.hpp b/src/hotspot/share/code/dependencyContext.hpp
--- a/src/hotspot/share/code/dependencyContext.hpp
+++ b/src/hotspot/share/code/dependencyContext.hpp
@@ -109,11 +109,11 @@
       _last_cleanup_addr(last_cleanup_addr) {}
 #endif // ASSERT
 
   static void init();
 
-  void mark_dependent_nmethods(DeoptimizationScope* deopt_scope, DepChange& changes);
+  void mark_dependent_nmethods(DeoptimizationScope* deopt_scope, DepChange& changes, InstanceKlass* context = nullptr);
   void add_dependent_nmethod(nmethod* nm);
   void remove_all_dependents();
   void remove_and_mark_for_deoptimization_all_dependents(DeoptimizationScope* deopt_scope);
   void clean_unloading_dependents();
   static nmethodBucket* release_and_get_next_not_unloading(nmethodBucket* b);
diff a/src/hotspot/share/code/exceptionHandlerTable.hpp b/src/hotspot/share/code/exceptionHandlerTable.hpp
--- a/src/hotspot/share/code/exceptionHandlerTable.hpp
+++ b/src/hotspot/share/code/exceptionHandlerTable.hpp
@@ -82,16 +82,21 @@
 // nmethod (for lookup purposes) in which case the table cannot be
 // modified.
 
 class nmethod;
 class ExceptionHandlerTable {
+  friend class SCCache;
+  friend class SCCReader;
+
  private:
   HandlerTableEntry* _table;    // the table
   int                _length;   // the current length of the table
   int                _size;     // the number of allocated entries
   ReallocMark        _nesting;  // assertion check for reallocations
 
+  int length() const { return _length; }
+  void set_length(int length) { _length = length; }
  public:
   // add the entry & grow the table if needed
   void add_entry(HandlerTableEntry entry);
   HandlerTableEntry* subtable_for(int catch_pco) const;
 
@@ -112,10 +117,11 @@
                                            // the lookup; zero (or null GrowableArray) indicates
                                            // innermost scope
     GrowableArray<intptr_t>* handler_pcos  // pc offsets for the compiled handlers
   );
 
+  HandlerTableEntry* table() const { return _table; }
   // nmethod support
   int  size_in_bytes() const { return align_up(_length * (int)sizeof(HandlerTableEntry), oopSize); }
   void copy_to(nmethod* nm);
   void copy_bytes_to(address addr);
 
@@ -139,16 +145,21 @@
 
 // Use 32-bit representation for offsets
 typedef  uint              implicit_null_entry;
 
 class ImplicitExceptionTable {
+  friend class SCCache;
+  friend class SCCReader;
+ private:
   uint _size;
   uint _len;
   implicit_null_entry *_data;
   implicit_null_entry *adr( uint idx ) const { return &_data[2*idx]; }
   ReallocMark          _nesting;  // assertion check for reallocations
 
+  void set_len(int length) { _len = length; }
+
 public:
   ImplicitExceptionTable( ) :  _size(0), _len(0), _data(nullptr) { }
   // (run-time) construction from nmethod
   ImplicitExceptionTable(const nmethod *nm);
 
@@ -166,10 +177,12 @@
   // value equals exec_off then the dispatch is expected to be a
   // deoptimization instead.
   uint continuation_offset( uint exec_off ) const;
 
   uint len() const { return _len; }
+  
+  implicit_null_entry* data() const { return _data; }
 
   uint get_exec_offset(uint i) { assert(i < _len, "oob"); return *adr(i); }
   uint get_cont_offset(uint i) { assert(i < _len, "oob"); return *(adr(i) + 1); }
 
   int size_in_bytes() const { return len() == 0 ? 0 : ((2 * len() + 1) * (int)sizeof(implicit_null_entry)); }
diff a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
--- a/src/hotspot/share/code/nmethod.cpp
+++ b/src/hotspot/share/code/nmethod.cpp
@@ -28,10 +28,11 @@
 #include "code/compiledIC.hpp"
 #include "code/dependencies.hpp"
 #include "code/nativeInst.hpp"
 #include "code/nmethod.inline.hpp"
 #include "code/scopeDesc.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/abstractCompiler.hpp"
 #include "compiler/compilationLog.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compileLog.hpp"
 #include "compiler/compileTask.hpp"
@@ -783,10 +784,12 @@
       klass = ((Klass*)md);
     } else if (md->is_method()) {
       klass = ((Method*)md)->method_holder();
     } else if (md->is_methodData()) {
       klass = ((MethodData*)md)->method()->method_holder();
+    } else if (md->is_methodCounters()) {
+      klass = ((MethodCounters*)md)->method()->method_holder();
     } else {
       md->print();
       ShouldNotReachHere();
     }
     assert(klass->is_loader_alive(), "must be alive");
@@ -1131,10 +1134,11 @@
   OopMapSet* oop_maps,
   ExceptionHandlerTable* handler_table,
   ImplicitExceptionTable* nul_chk_table,
   AbstractCompiler* compiler,
   CompLevel comp_level
+  , SCCEntry* scc_entry
 #if INCLUDE_JVMCI
   , char* speculations,
   int speculations_len,
   JVMCINMethodData* jvmci_data
 #endif
@@ -1175,11 +1179,11 @@
 
     nm = new (nmethod_size, comp_level)
     nmethod(method(), compiler->type(), nmethod_size, immutable_data_size,
             compile_id, entry_bci, immutable_data, offsets, orig_pc_offset,
             debug_info, dependencies, code_buffer, frame_size, oop_maps,
-            handler_table, nul_chk_table, compiler, comp_level
+            handler_table, nul_chk_table, compiler, comp_level, scc_entry
 #if INCLUDE_JVMCI
             , speculations,
             speculations_len,
             jvmci_data
 #endif
@@ -1211,10 +1215,22 @@
       NOT_PRODUCT(if (nm != nullptr)  note_java_nmethod(nm));
     }
   }
   // Do verification and logging outside CodeCache_lock.
   if (nm != nullptr) {
+
+#ifdef ASSERT
+    LogTarget(Debug, scc, nmethod) log;
+    if (log.is_enabled()) {
+      LogStream out(log);
+      out.print_cr("== new_nmethod 2");
+      FlagSetting fs(PrintRelocations, true);
+      nm->print(&out);
+      nm->decode(&out);
+    }
+#endif
+
     // Safepoints in nmethod::verify aren't allowed because nm hasn't been installed yet.
     DEBUG_ONLY(nm->verify();)
     nm->log_new_nmethod();
   }
   return nm;
@@ -1237,11 +1253,14 @@
   _has_monitors               = 0;
   _has_scoped_access          = 0;
   _has_flushed_dependencies   = 0;
   _is_unlinked                = 0;
   _load_reported              = 0; // jvmti state
+  _preloaded                  = 0;
+  _has_clinit_barriers        = 0;
 
+  _used                       = false;
   _deoptimization_status      = not_marked;
 
   // SECT_CONSTS is first in code buffer so the offset should be 0.
   int consts_offset = code_buffer->total_offset_of(code_buffer->consts());
   assert(consts_offset == 0, "const_offset: %d", consts_offset);
@@ -1309,10 +1328,12 @@
     }
     // Native wrappers do not have deopt handlers. Make the values
     // something that will never match a pc like the nmethod vtable entry
     _deopt_handler_offset    = 0;
     _deopt_mh_handler_offset = 0;
+    _scc_entry               = nullptr;
+    _method_profiling_count  = 0;
     _unwind_handler_offset   = 0;
 
     CHECKED_CAST(_metadata_offset, uint16_t, (align_up(code_buffer->total_oop_size(), oopSize)));
     int data_end_offset = _metadata_offset + align_up(code_buffer->total_metadata_size(), wordSize);
 #if INCLUDE_JVMCI
@@ -1369,11 +1390,11 @@
       print(); // print the header part only.
     }
 #if defined(SUPPORT_DATA_STRUCTS)
     if (AbstractDisassembler::show_structs()) {
       if (PrintRelocations) {
-        print_relocations();
+        print_relocations_on(tty);
         tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
       }
     }
 #endif
     if (xtty != nullptr) {
@@ -1412,10 +1433,11 @@
   OopMapSet* oop_maps,
   ExceptionHandlerTable* handler_table,
   ImplicitExceptionTable* nul_chk_table,
   AbstractCompiler* compiler,
   CompLevel comp_level
+  , SCCEntry* scc_entry
 #if INCLUDE_JVMCI
   , char* speculations,
   int speculations_len,
   JVMCINMethodData* jvmci_data
 #endif
@@ -1431,10 +1453,12 @@
   {
     debug_only(NoSafepointVerifier nsv;)
     assert_locked_or_safepoint(CodeCache_lock);
 
     init_defaults(code_buffer, offsets);
+    _scc_entry      = scc_entry;
+    _method_profiling_count  = 0;
 
     _osr_entry_point = code_begin() + offsets->value(CodeOffsets::OSR_Entry);
     _entry_bci       = entry_bci;
     _compile_id      = compile_id;
     _comp_level      = comp_level;
@@ -1562,16 +1586,17 @@
 }
 
 // Print a short set of xml attributes to identify this nmethod.  The
 // output should be embedded in some other element.
 void nmethod::log_identity(xmlStream* log) const {
-  log->print(" compile_id='%d'", compile_id());
+  assert(log->inside_attrs_or_error(), "printing attributes");
+  log->print(" code_compile_id='%d'", compile_id());
   const char* nm_kind = compile_kind();
-  if (nm_kind != nullptr)  log->print(" compile_kind='%s'", nm_kind);
-  log->print(" compiler='%s'", compiler_name());
+  if (nm_kind != nullptr)  log->print(" code_compile_kind='%s'", nm_kind);
+  log->print(" code_compiler='%s'", compiler_name());
   if (TieredCompilation) {
-    log->print(" level='%d'", comp_level());
+    log->print(" code_compile_level='%d'", comp_level());
   }
 #if INCLUDE_JVMCI
   if (jvmci_nmethod_data() != nullptr) {
     const char* jvmci_name = jvmci_nmethod_data()->name();
     if (jvmci_name != nullptr) {
@@ -1688,11 +1713,11 @@
     if (printmethod || PrintDebugInfo || CompilerOracle::has_option(mh, CompileCommandEnum::PrintDebugInfo)) {
       print_scopes();
       tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
     }
     if (printmethod || PrintRelocations || CompilerOracle::has_option(mh, CompileCommandEnum::PrintRelocations)) {
-      print_relocations();
+      print_relocations_on(tty);
       tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
     }
     if (printmethod || PrintDependencies || CompilerOracle::has_option(mh, CompileCommandEnum::PrintDependencies)) {
       print_dependencies_on(tty);
       tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
@@ -1936,10 +1961,18 @@
   if (mdo == nullptr)  return;
   // There is a benign race here.  See comments in methodData.hpp.
   mdo->inc_decompile_count();
 }
 
+void nmethod::inc_method_profiling_count() {
+  Atomic::inc(&_method_profiling_count);
+}
+
+uint64_t nmethod::method_profiling_count() {
+  return _method_profiling_count;
+}
+
 bool nmethod::try_transition(signed char new_state_int) {
   signed char new_state = new_state_int;
   assert_lock_strong(NMethodState_lock);
   signed char old_state = _state;
   if (old_state >= new_state) {
@@ -1981,11 +2014,11 @@
     method()->unlink_code(this);
   }
 }
 
 // Invalidate code
-bool nmethod::make_not_entrant() {
+bool nmethod::make_not_entrant(bool make_not_entrant) {
   // This can be called while the system is already at a safepoint which is ok
   NoSafepointVerifier nsv;
 
   if (is_unloading()) {
     // If the nmethod is unloading, then it is already not entrant through
@@ -2044,10 +2077,17 @@
     log_state_change();
 
     // Remove nmethod from method.
     unlink_from_method();
 
+    if (make_not_entrant) {
+      // Keep cached code if it was simply replaced
+      // otherwise make it not entrant too.
+      SCCache::invalidate(_scc_entry);
+    }
+
+    CompileBroker::log_not_entrant(this);
   } // leave critical region under NMethodState_lock
 
 #if INCLUDE_JVMCI
   // Invalidate can't occur while holding the NMethodState_lock
   JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
@@ -2183,10 +2223,16 @@
   task->mark_success();
   task->set_nm_content_size(content_size());
   task->set_nm_insts_size(insts_size());
   task->set_nm_total_size(total_size());
 
+  // task->is_scc() is true only for loaded cached code.
+  // nmethod::_scc_entry is set for loaded and stored cached code
+  // to invalidate the entry when nmethod is deoptimized.
+  // There is option to not store in archive cached code.
+  guarantee((_scc_entry != nullptr) || !task->is_scc() || VerifyCachedCode, "sanity");
+
   // JVMTI -- compiled method notification (must be done outside lock)
   post_compiled_method_load_event();
 
   if (CompilationLog::log() != nullptr) {
     CompilationLog::log()->log_nmethod(JavaThread::current(), this);
@@ -3121,10 +3167,13 @@
   if (speculations_size () > 0) st->print_cr(" speculations   [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
                                              p2i(speculations_begin()),
                                              p2i(speculations_end()),
                                              speculations_size());
 #endif
+  if (SCCache::is_on() && _scc_entry != nullptr) {
+    _scc_entry->print(st);
+  }
 }
 
 void nmethod::print_code() {
   ResourceMark m;
   ttyLocker ttyl;
@@ -3220,15 +3269,15 @@
   }
 }
 #endif
 
 #ifndef PRODUCT  // RelocIterator does support printing only then.
-void nmethod::print_relocations() {
+void nmethod::print_relocations_on(outputStream* st) {
   ResourceMark m;       // in case methods get printed via the debugger
-  tty->print_cr("relocations:");
+  st->print_cr("relocations:");
   RelocIterator iter(this);
-  iter.print();
+  iter.print_on(st);
 }
 #endif
 
 void nmethod::print_pcs_on(outputStream* st) {
   ResourceMark m;       // in case methods get printed via debugger
@@ -3579,10 +3628,20 @@
         case relocInfo::runtime_call_w_cp_type: {
           stringStream st;
           st.print("runtime_call");
           CallRelocation* r = (CallRelocation*)iter.reloc();
           address dest = r->destination();
+          if (StubRoutines::contains(dest)) {
+            StubCodeDesc* desc = StubCodeDesc::desc_for(dest);
+            if (desc == nullptr) {
+              desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset);
+            }
+            if (desc != nullptr) {
+              st.print(" Stub::%s", desc->name());
+              return st.as_string();
+            }
+          }
           CodeBlob* cb = CodeCache::find_blob(dest);
           if (cb != nullptr) {
             st.print(" %s", cb->name());
           } else {
             ResourceMark rm;
diff a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp
--- a/src/hotspot/share/code/nmethod.hpp
+++ b/src/hotspot/share/code/nmethod.hpp
@@ -43,10 +43,11 @@
 class ImplicitExceptionTable;
 class JvmtiThreadState;
 class MetadataClosure;
 class NativeCallWrapper;
 class OopIterateClosure;
+class SCCEntry;
 class ScopeDesc;
 class xmlStream;
 
 // This class is used internally by nmethods, to cache
 // exception/pc/handler information.
@@ -173,10 +174,13 @@
   // Used to track in which deoptimize handshake this method will be deoptimized.
   uint64_t  _deoptimization_generation;
 
   uint64_t  _gc_epoch;
 
+  // Profiling counter used to figure out the hottest nmethods to record into CDS
+  volatile uint64_t _method_profiling_count;
+
   Method*   _method;
 
   // To reduce header size union fields which usages do not overlap.
   union {
     // To support simple linked-list chaining of nmethods:
@@ -258,10 +262,14 @@
 
   int          _compile_id;            // which compilation made this nmethod
   CompLevel    _comp_level;            // compilation level (s1)
   CompilerType _compiler_type;         // which compiler made this nmethod (u1)
 
+  SCCEntry* _scc_entry;
+
+  bool _used; // has this nmethod ever been invoked?
+
   // Local state used to keep track of whether unloading is happening or not
   volatile uint8_t _is_unloading_state;
 
   // Protected by NMethodState_lock
   volatile signed char _state;         // {not_installed, in_use, not_entrant}
@@ -272,11 +280,13 @@
           _has_wide_vectors:1,         // Preserve wide vectors at safepoints
           _has_monitors:1,             // Fastpath monitor detection for continuations
           _has_scoped_access:1,        // used by for shared scope closure (scopedMemoryAccess.cpp)
           _has_flushed_dependencies:1, // Used for maintenance of dependencies (under CodeCache_lock)
           _is_unlinked:1,              // mark during class unloading
-          _load_reported:1;            // used by jvmti to track if an event has been posted for this nmethod
+          _load_reported:1,            // used by jvmti to track if an event has been posted for this nmethod
+          _preloaded:1,
+          _has_clinit_barriers:1;
 
   enum DeoptimizationStatus : u1 {
     not_marked,
     deoptimize,
     deoptimize_noupdate,
@@ -324,10 +334,11 @@
           OopMapSet* oop_maps,
           ExceptionHandlerTable* handler_table,
           ImplicitExceptionTable* nul_chk_table,
           AbstractCompiler* compiler,
           CompLevel comp_level
+          , SCCEntry* scc_entry
 #if INCLUDE_JVMCI
           , char* speculations = nullptr,
           int speculations_len = 0,
           JVMCINMethodData* jvmci_data = nullptr
 #endif
@@ -482,10 +493,11 @@
                               OopMapSet* oop_maps,
                               ExceptionHandlerTable* handler_table,
                               ImplicitExceptionTable* nul_chk_table,
                               AbstractCompiler* compiler,
                               CompLevel comp_level
+                              , SCCEntry* scc_entry
 #if INCLUDE_JVMCI
                               , char* speculations = nullptr,
                               int speculations_len = 0,
                               JVMCINMethodData* jvmci_data = nullptr
 #endif
@@ -622,19 +634,22 @@
   // Heuristically deduce an nmethod isn't worth keeping around
   bool is_cold();
   bool is_unloading();
   void do_unloading(bool unloading_occurred);
 
+  void inc_method_profiling_count();
+  uint64_t method_profiling_count();
+
   bool make_in_use() {
     return try_transition(in_use);
   }
   // Make the nmethod non entrant. The nmethod will continue to be
   // alive.  It is used when an uncommon trap happens.  Returns true
   // if this thread changed the state of the nmethod or false if
   // another thread performed the transition.
-  bool  make_not_entrant();
-  bool  make_not_used()    { return make_not_entrant(); }
+  bool  make_not_entrant(bool make_not_entrant = true);
+  bool  make_not_used() { return make_not_entrant(false); }
 
   bool  is_marked_for_deoptimization() const { return deoptimization_status() != not_marked; }
   bool  has_been_deoptimized() const { return deoptimization_status() == deoptimize_done; }
   void  set_deoptimized_done();
 
@@ -672,10 +687,16 @@
   void  set_has_method_handle_invokes(bool z)     { _has_method_handle_invokes = z; }
 
   bool  has_wide_vectors() const                  { return _has_wide_vectors; }
   void  set_has_wide_vectors(bool z)              { _has_wide_vectors = z; }
 
+  bool  has_clinit_barriers() const               { return _has_clinit_barriers; }
+  void  set_has_clinit_barriers(bool z)           { _has_clinit_barriers = z; }
+
+  bool  preloaded() const                         { return _preloaded; }
+  void  set_preloaded(bool z)                     { _preloaded = z; }
+
   bool  has_flushed_dependencies() const          { return _has_flushed_dependencies; }
   void  set_has_flushed_dependencies(bool z)      {
     assert(!has_flushed_dependencies(), "should only happen once");
     _has_flushed_dependencies = z;
   }
@@ -891,10 +912,16 @@
   void copy_scopes_pcs(PcDesc* pcs, int count);
   void copy_scopes_data(address buffer, int size);
 
   int orig_pc_offset() { return _orig_pc_offset; }
 
+  SCCEntry* scc_entry() const { return _scc_entry; }
+  bool is_scc() const { return scc_entry() != nullptr; }
+
+  bool     used() const { return _used; }
+  void set_used()       { _used = true; }
+
   // Post successful compilation
   void post_compiled_method(CompileTask* task);
 
   // jvmti support:
   void post_compiled_method_load_event(JvmtiThreadState* state = nullptr);
@@ -916,11 +943,11 @@
   void print(outputStream* st) const;
   void print_code();
 
 #if defined(SUPPORT_DATA_STRUCTS)
   // print output in opt build for disassembler library
-  void print_relocations()                        PRODUCT_RETURN;
+  void print_relocations_on(outputStream* st)     PRODUCT_RETURN;
   void print_pcs_on(outputStream* st);
   void print_scopes() { print_scopes_on(tty); }
   void print_scopes_on(outputStream* st)          PRODUCT_RETURN;
   void print_value_on(outputStream* st) const override;
   void print_handler_table();
diff a/src/hotspot/share/code/oopRecorder.cpp b/src/hotspot/share/code/oopRecorder.cpp
--- a/src/hotspot/share/code/oopRecorder.cpp
+++ b/src/hotspot/share/code/oopRecorder.cpp
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #include "ci/ciEnv.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciMetadata.hpp"
+#include "ci/ciUtilities.hpp"
 #include "code/oopRecorder.inline.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "memory/allocation.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/jniHandles.inline.hpp"
@@ -299,11 +300,13 @@
       int n = extern_hist->at(index);
       if (n > 0) {
         address addr = at(index);
         tty->print("%d: %8d " INTPTR_FORMAT " :", j++, n, p2i(addr));
         if (addr != nullptr) {
-          if (StubRoutines::contains(addr)) {
+          if (is_card_table_address(addr)) {
+            tty->print(" card_table_base");
+          } else if (StubRoutines::contains(addr)) {
             StubCodeDesc* desc = StubCodeDesc::desc_for(addr);
             if (desc == nullptr) {
               desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset);
             }
             const char* stub_name = (desc != nullptr) ? desc->name() : "<unknown>";
diff a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp
--- a/src/hotspot/share/code/relocInfo.cpp
+++ b/src/hotspot/share/code/relocInfo.cpp
@@ -21,14 +21,16 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciUtilities.hpp"
 #include "code/codeCache.hpp"
 #include "code/compiledIC.hpp"
 #include "code/nmethod.hpp"
 #include "code/relocInfo.hpp"
+#include "code/SCCache.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/flags/flagSetting.hpp"
@@ -171,10 +173,13 @@
   set_limits(begin, limit);
 }
 
 bool RelocIterator::addr_in_const() const {
   const int n = CodeBuffer::SECT_CONSTS;
+  if (_section_start[n] == nullptr) {
+    return false;
+  }
   return section_start(n) <= addr() && addr() < section_end(n);
 }
 
 
 void RelocIterator::set_limits(address begin, address limit) {
@@ -461,15 +466,16 @@
 }
 
 void external_word_Relocation::pack_data_to(CodeSection* dest) {
   short* p = (short*) dest->locs_end();
   int index = ExternalsRecorder::find_index(_target);
-  p = pack_1_int_to(p, index);
+  // Use 4 bytes to store index to be able patch it when
+  // updating relocations in SCCReader::read_relocations().
+  p = add_jint(p, index);
   dest->set_locs_end((relocInfo*) p);
 }
 
-
 void external_word_Relocation::unpack_data() {
   int index = unpack_1_int();
   _target = ExternalsRecorder::at(index);
 }
 
@@ -734,10 +740,18 @@
     set_value(_target);
   }
   // If target is nullptr, this is  an absolute embedded reference to an external
   // location, which means  there is nothing to fix here.  In either case, the
   // resulting target should be an "external" address.
+#ifdef ASSERT
+  if (SCCache::is_on()) {
+    // SCA needs relocation info for card table base which may point to CodeCache
+    if (is_card_table_address(target())) {
+      return;
+    }
+  }
+#endif
   postcond(src->section_index_of(target()) == CodeBuffer::SECT_NONE);
   postcond(dest->section_index_of(target()) == CodeBuffer::SECT_NONE);
 }
 
 
@@ -769,16 +783,11 @@
     }
   }
   return target;
 }
 
-//---------------------------------------------------------------------------------
-// Non-product code
-
-#ifndef PRODUCT
-
-static const char* reloc_type_string(relocInfo::relocType t) {
+const char* relocInfo::type_name(relocInfo::relocType t) {
   switch (t) {
   #define EACH_CASE(name) \
   case relocInfo::name##_type: \
     return #name;
 
@@ -793,29 +802,29 @@
     return "UNKNOWN RELOC TYPE";
   }
 }
 
 
-void RelocIterator::print_current() {
+void RelocIterator::print_current_on(outputStream* st) {
   if (!has_current()) {
-    tty->print_cr("(no relocs)");
+    st->print_cr("(no relocs)");
     return;
   }
-  tty->print("relocInfo@" INTPTR_FORMAT " [type=%d(%s) addr=" INTPTR_FORMAT " offset=%d",
-             p2i(_current), type(), reloc_type_string((relocInfo::relocType) type()), p2i(_addr), _current->addr_offset());
+  st->print("relocInfo@" INTPTR_FORMAT " [type=%d(%s) addr=" INTPTR_FORMAT " offset=%d",
+            p2i(_current), type(), relocInfo::type_name(type()), p2i(_addr), _current->addr_offset());
   if (current()->format() != 0)
-    tty->print(" format=%d", current()->format());
+    st->print(" format=%d", current()->format());
   if (datalen() == 1) {
-    tty->print(" data=%d", data()[0]);
+    st->print(" data=%d", data()[0]);
   } else if (datalen() > 0) {
-    tty->print(" data={");
+    st->print(" data={");
     for (int i = 0; i < datalen(); i++) {
-      tty->print("%04x", data()[i] & 0xFFFF);
+      st->print("%04x", data()[i] & 0xFFFF);
     }
-    tty->print("}");
+    st->print("}");
   }
-  tty->print("]");
+  st->print("]");
   switch (type()) {
   case relocInfo::oop_type:
     {
       oop_Relocation* r = oop_reloc();
       oop* oop_addr  = nullptr;
@@ -824,18 +833,18 @@
       if (code() != nullptr || r->oop_is_immediate()) {
         oop_addr  = r->oop_addr();
         raw_oop   = *oop_addr;
         oop_value = r->oop_value();
       }
-      tty->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]",
-                 p2i(oop_addr), p2i(raw_oop));
+      st->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT " index=%d]",
+                 p2i(oop_addr), p2i(raw_oop), r->oop_index());
       // Do not print the oop by default--we want this routine to
       // work even during GC or other inconvenient times.
       if (WizardMode && oop_value != nullptr) {
-        tty->print("oop_value=" INTPTR_FORMAT ": ", p2i(oop_value));
+        st->print("oop_value=" INTPTR_FORMAT ": ", p2i(oop_value));
         if (oopDesc::is_oop(oop_value)) {
-          oop_value->print_value_on(tty);
+          oop_value->print_value_on(st);
         }
       }
       break;
     }
   case relocInfo::metadata_type:
@@ -847,108 +856,151 @@
       if (code() != nullptr || r->metadata_is_immediate()) {
         metadata_addr  = r->metadata_addr();
         raw_metadata   = *metadata_addr;
         metadata_value = r->metadata_value();
       }
-      tty->print(" | [metadata_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]",
-                 p2i(metadata_addr), p2i(raw_metadata));
+      st->print(" | [metadata_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT " index=%d]",
+                 p2i(metadata_addr), p2i(raw_metadata), r->metadata_index());
       if (metadata_value != nullptr) {
-        tty->print("metadata_value=" INTPTR_FORMAT ": ", p2i(metadata_value));
-        metadata_value->print_value_on(tty);
+        st->print("metadata_value=" INTPTR_FORMAT ": ", p2i(metadata_value));
+        metadata_value->print_value_on(st);
       }
       break;
     }
   case relocInfo::external_word_type:
   case relocInfo::internal_word_type:
   case relocInfo::section_word_type:
     {
       DataRelocation* r = (DataRelocation*) reloc();
-      tty->print(" | [target=" INTPTR_FORMAT "]", p2i(r->value())); //value==target
+      st->print(" | [target=" INTPTR_FORMAT "]", p2i(r->value())); //value==target
       break;
     }
   case relocInfo::static_call_type:
     {
       static_call_Relocation* r = (static_call_Relocation*) reloc();
-      tty->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
+      st->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
                  p2i(r->destination()), p2i(r->method_value()));
+      CodeBlob* cb = CodeCache::find_blob(r->destination());
+      if (cb != nullptr) {
+        st->print(" Blob::%s", cb->name());
+      }
       break;
     }
   case relocInfo::runtime_call_type:
   case relocInfo::runtime_call_w_cp_type:
     {
       CallRelocation* r = (CallRelocation*) reloc();
-      tty->print(" | [destination=" INTPTR_FORMAT "]", p2i(r->destination()));
+      address dest = r->destination();
+      st->print(" | [destination=" INTPTR_FORMAT "]", p2i(dest));
+      if (StubRoutines::contains(dest)) {
+        StubCodeDesc* desc = StubCodeDesc::desc_for(dest);
+        if (desc == nullptr) {
+          desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset);
+        }
+        if (desc != nullptr) {
+          st->print(" Stub::%s", desc->name());
+        }
+      } else {
+        CodeBlob* cb = CodeCache::find_blob(dest);
+        if (cb != nullptr) {
+          st->print(" Blob::%s", cb->name());
+        } else {
+          ResourceMark rm;
+          const int buflen = 1024;
+          char* buf = NEW_RESOURCE_ARRAY(char, buflen);
+          int offset;
+          if (os::dll_address_to_function_name(dest, buf, buflen, &offset)) {
+            st->print(" %s", buf);
+            if (offset != 0) {
+              st->print("+%d", offset);
+            }
+          }
+        }
+      }
       break;
     }
   case relocInfo::virtual_call_type:
     {
       virtual_call_Relocation* r = (virtual_call_Relocation*) reloc();
-      tty->print(" | [destination=" INTPTR_FORMAT " cached_value=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
-                 p2i(r->destination()), p2i(r->cached_value()), p2i(r->method_value()));
+      st->print(" | [destination=" INTPTR_FORMAT " cached_value=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
+                p2i(r->destination()), p2i(r->cached_value()), p2i(r->method_value()));
+      CodeBlob* cb = CodeCache::find_blob(r->destination());
+      if (cb != nullptr) {
+        st->print(" Blob::%s", cb->name());
+      }
       break;
     }
   case relocInfo::static_stub_type:
     {
       static_stub_Relocation* r = (static_stub_Relocation*) reloc();
-      tty->print(" | [static_call=" INTPTR_FORMAT "]", p2i(r->static_call()));
+      st->print(" | [static_call=" INTPTR_FORMAT "]", p2i(r->static_call()));
       break;
     }
   case relocInfo::trampoline_stub_type:
     {
       trampoline_stub_Relocation* r = (trampoline_stub_Relocation*) reloc();
-      tty->print(" | [trampoline owner=" INTPTR_FORMAT "]", p2i(r->owner()));
+      st->print(" | [trampoline owner=" INTPTR_FORMAT "]", p2i(r->owner()));
       break;
     }
   case relocInfo::opt_virtual_call_type:
     {
       opt_virtual_call_Relocation* r = (opt_virtual_call_Relocation*) reloc();
-      tty->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
+      st->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
                  p2i(r->destination()), p2i(r->method_value()));
+      CodeBlob* cb = CodeCache::find_blob(r->destination());
+      if (cb != nullptr) {
+        st->print(" Blob::%s", cb->name());
+      }
       break;
     }
   default:
     break;
   }
-  tty->cr();
+  st->cr();
 }
 
 
-void RelocIterator::print() {
+void RelocIterator::print_on(outputStream* st) {
   RelocIterator save_this = (*this);
   relocInfo* scan = _current;
   if (!has_current())  scan += 1;  // nothing to scan here!
 
   bool skip_next = has_current();
   bool got_next;
   while (true) {
     got_next = (skip_next || next());
     skip_next = false;
 
-    tty->print("         @" INTPTR_FORMAT ": ", p2i(scan));
+    st->print("         @" INTPTR_FORMAT ": ", p2i(scan));
     relocInfo* newscan = _current+1;
     if (!has_current())  newscan -= 1;  // nothing to scan here!
     while (scan < newscan) {
-      tty->print("%04x", *(short*)scan & 0xFFFF);
+      st->print("%04x", *(short*)scan & 0xFFFF);
       scan++;
     }
-    tty->cr();
+    st->cr();
 
     if (!got_next)  break;
-    print_current();
+    print_current_on(st);
   }
 
   (*this) = save_this;
 }
 
+//---------------------------------------------------------------------------------
+// Non-product code
+
+#ifndef PRODUCT
+
 // For the debugger:
 extern "C"
 void print_blob_locs(nmethod* nm) {
   nm->print();
   RelocIterator iter(nm);
-  iter.print();
+  iter.print_on(tty);
 }
 extern "C"
 void print_buf_locs(CodeBuffer* cb) {
   FlagSetting fs(PrintRelocations, true);
-  cb->print();
+  cb->print_on(tty);
 }
 #endif // !PRODUCT
diff a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp
--- a/src/hotspot/share/code/relocInfo.hpp
+++ b/src/hotspot/share/code/relocInfo.hpp
@@ -449,10 +449,12 @@
     // of any relocation record.
     // Extended format is length prefix, data words, and tag/offset suffix.
     length_limit       = 1 + 1 + (3*BytesPerWord/BytesPerShort) + 1,
     have_format        = format_width > 0
   };
+
+  static const char* type_name(relocInfo::relocType t);
 };
 
 #define FORWARD_DECLARE_EACH_CLASS(name)              \
 class name##_Relocation;
 APPLY_TO_RELOCATIONS(FORWARD_DECLARE_EACH_CLASS)
@@ -636,15 +638,15 @@
   int          datalen()      const { return _datalen; }
   bool     has_current()      const { return _datalen >= 0; }
   bool   addr_in_const()      const;
 
   address section_start(int n) const {
-    assert(_section_start[n], "must be initialized");
+    assert(_section_start[n], "section %d must be initialized", n);
     return _section_start[n];
   }
   address section_end(int n) const {
-    assert(_section_end[n], "must be initialized");
+    assert(_section_end[n], "section %d must be initialized", n);
     return _section_end[n];
   }
 
   // The address points to the affected displacement part of the instruction.
   // For RISC, this is just the whole instruction.
@@ -656,24 +658,23 @@
   APPLY_TO_RELOCATIONS(EACH_TYPE)
   #undef EACH_TYPE
   // generic relocation accessor; switches on type to call the above
   Relocation* reloc();
 
-#ifndef PRODUCT
  public:
-  void print();
-  void print_current();
-#endif
+  void print_on(outputStream* st);
+  void print_current_on(outputStream* st);
 };
 
 
 // A Relocation is a flyweight object allocated within a RelocationHolder.
 // It represents the relocation data of relocation record.
 // So, the RelocIterator unpacks relocInfos into Relocations.
 
 class Relocation {
   friend class RelocIterator;
+  friend class SCCReader;
 
  private:
   // When a relocation has been created by a RelocIterator,
   // this field is non-null.  It allows the relocation to know
   // its context, such as the address to which it applies.
@@ -1285,10 +1286,29 @@
   // Return the address of the NativeCall that owns the trampoline.
   address owner() { return _owner; }
 
   void pack_data_to(CodeSection * dest) override;
   void unpack_data() override;
+#if defined(AARCH64)
+  address    pd_destination     ();
+  void       pd_set_destination (address x);
+#endif
+  address  destination() {
+#if defined(AARCH64)
+    return pd_destination();
+#else
+    fatal("trampoline_stub_Relocation::destination() unimplemented");
+    return (address)-1;
+#endif
+  }
+  void     set_destination(address x) {
+#if defined(AARCH64)
+    pd_set_destination(x);
+#else
+    fatal("trampoline_stub_Relocation::set_destination() unimplemented");
+#endif
+  }
 
   // Find the trampoline stub for a call.
   static address get_trampoline_for(address call, nmethod* code);
 };
 
diff a/src/hotspot/share/compiler/abstractCompiler.hpp b/src/hotspot/share/compiler/abstractCompiler.hpp
--- a/src/hotspot/share/compiler/abstractCompiler.hpp
+++ b/src/hotspot/share/compiler/abstractCompiler.hpp
@@ -33,19 +33,20 @@
 
 // Per-compiler statistics
 class CompilerStatistics {
   friend class VMStructs;
 
+ public:
   class Data {
     friend class VMStructs;
   public:
     elapsedTimer _time;  // time spent compiling
     uint _bytes;         // number of bytecodes compiled, including inlined bytecodes
     uint _count;         // number of compilations
     Data() : _bytes(0), _count(0) {}
     void update(elapsedTimer time, int bytes) {
-      _time.add(time);
+      _time.add(&time);
       _bytes += bytes;
       _count++;
     }
     void reset() {
       _time.reset();
@@ -53,10 +54,13 @@
   };
 
  public:
   Data _standard;  // stats for non-OSR compilations
   Data _osr;       // stats for OSR compilations
+  Data _bailout;
+  Data _invalidated;
+  Data _made_not_entrant;
   uint _nmethods_size; //
   uint _nmethods_code_size;
 
   double total_time() { return _standard._time.seconds() + _osr._time.seconds(); }
 
diff a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp
--- a/src/hotspot/share/compiler/compilationPolicy.cpp
+++ b/src/hotspot/share/compiler/compilationPolicy.cpp
@@ -21,19 +21,23 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotLinkedClassBulkLoader.hpp"
 #include "code/scopeDesc.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/compilerOracle.hpp"
+#include "compiler/recompilationPolicy.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/methodData.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/frame.hpp"
 #include "runtime/frame.inline.hpp"
@@ -49,15 +53,19 @@
 #endif
 #if INCLUDE_JVMCI
 #include "jvmci/jvmci.hpp"
 #endif
 
-jlong CompilationPolicy::_start_time = 0;
+int64_t CompilationPolicy::_start_time = 0;
 int CompilationPolicy::_c1_count = 0;
 int CompilationPolicy::_c2_count = 0;
+int CompilationPolicy::_c3_count = 0;
+int CompilationPolicy::_sc_count = 0;
 double CompilationPolicy::_increase_threshold_at_ratio = 0;
 
+CompilationPolicy::TrainingReplayQueue CompilationPolicy::_training_replay_queue;
+
 void compilationPolicy_init() {
   CompilationPolicy::initialize();
 }
 
 int CompilationPolicy::compiler_count(CompLevel comp_level) {
@@ -77,37 +85,171 @@
   if (ReplayCompiles) return false;
 
   if (m->has_compiled_code()) return false;       // already compiled
   if (!can_be_compiled(m, comp_level)) return false;
 
-  return !UseInterpreter ||                                              // must compile all methods
+  return !UseInterpreter ||                                                                        // must compile all methods
          (AlwaysCompileLoopMethods && m->has_loops() && CompileBroker::should_compile_new_jobs()); // eagerly compile loop methods
 }
 
+void CompilationPolicy::maybe_compile_early(const methodHandle& m, TRAPS) {
+  if (m->method_holder()->is_not_initialized()) {
+    // 'is_not_initialized' means not only '!is_initialized', but also that
+    // initialization has not been started yet ('!being_initialized')
+    // Do not force compilation of methods in uninitialized classes.
+    return;
+  }
+  if (!m->is_native() && MethodTrainingData::have_data()) {
+    MethodTrainingData* mtd = MethodTrainingData::find(m);
+    if (mtd == nullptr) {
+      return;              // there is no training data recorded for m
+    }
+    bool recompile = m->code_has_clinit_barriers();
+    CompLevel cur_level = static_cast<CompLevel>(m->highest_comp_level());
+    CompLevel next_level = trained_transition(m, cur_level, mtd, THREAD);
+    if ((next_level != cur_level || recompile) && can_be_compiled(m, next_level) && !CompileBroker::compilation_is_in_queue(m)) {
+      bool requires_online_compilation = false;
+      CompileTrainingData* ctd = mtd->last_toplevel_compile(next_level);
+      if (ctd != nullptr) {
+        requires_online_compilation = (ctd->init_deps_left() > 0);
+      }
+      if (requires_online_compilation && recompile) {
+        return;
+      }
+      if (PrintTieredEvents) {
+        print_event(FORCE_COMPILE, m(), m(), InvocationEntryBci, next_level);
+      }
+      CompileBroker::compile_method(m, InvocationEntryBci, next_level, methodHandle(), 0, requires_online_compilation, CompileTask::Reason_MustBeCompiled, THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        CLEAR_PENDING_EXCEPTION;
+      }
+    }
+  }
+}
+
+void CompilationPolicy::maybe_compile_early_after_init(const methodHandle& m, TRAPS) {
+  assert(m->method_holder()->is_initialized(), "Should be called after class initialization");
+  maybe_compile_early(m, THREAD);
+}
+
 void CompilationPolicy::compile_if_required(const methodHandle& m, TRAPS) {
+  if (!THREAD->can_call_java() || THREAD->is_Compiler_thread()) {
+    // don't force compilation, resolve was on behalf of compiler
+    return;
+  }
+  if (m->method_holder()->is_not_initialized()) {
+    // 'is_not_initialized' means not only '!is_initialized', but also that
+    // initialization has not been started yet ('!being_initialized')
+    // Do not force compilation of methods in uninitialized classes.
+    // Note that doing this would throw an assert later,
+    // in CompileBroker::compile_method.
+    // We sometimes use the link resolver to do reflective lookups
+    // even before classes are initialized.
+    return;
+  }
+
   if (must_be_compiled(m)) {
     // This path is unusual, mostly used by the '-Xcomp' stress test mode.
+    CompLevel level = initial_compile_level(m);
+    if (PrintTieredEvents) {
+      print_event(FORCE_COMPILE, m(), m(), InvocationEntryBci, level);
+    }
+    CompileBroker::compile_method(m, InvocationEntryBci, level, methodHandle(), 0, false, CompileTask::Reason_MustBeCompiled, THREAD);
+  }
+}
 
-    if (!THREAD->can_call_java() || THREAD->is_Compiler_thread()) {
-      // don't force compilation, resolve was on behalf of compiler
-      return;
+void CompilationPolicy::replay_training_at_init_impl(InstanceKlass* klass, TRAPS) {
+  if (!klass->has_init_deps_processed()) {
+    ResourceMark rm;
+    log_debug(training)("Replay training: %s", klass->external_name());
+
+    KlassTrainingData* ktd = KlassTrainingData::find(klass);
+    if (ktd != nullptr) {
+      guarantee(ktd->has_holder(), "");
+      ktd->notice_fully_initialized(); // sets klass->has_init_deps_processed bit
+      assert(klass->has_init_deps_processed(), "");
+
+      ktd->iterate_all_comp_deps([&](CompileTrainingData* ctd) {
+        if (ctd->init_deps_left() == 0) {
+          MethodTrainingData* mtd = ctd->method();
+          if (mtd->has_holder()) {
+            const methodHandle mh(THREAD, const_cast<Method*>(mtd->holder()));
+            CompilationPolicy::maybe_compile_early(mh, THREAD);
+          }
+        }
+      });
     }
-    if (m->method_holder()->is_not_initialized()) {
-      // 'is_not_initialized' means not only '!is_initialized', but also that
-      // initialization has not been started yet ('!being_initialized')
-      // Do not force compilation of methods in uninitialized classes.
-      // Note that doing this would throw an assert later,
-      // in CompileBroker::compile_method.
-      // We sometimes use the link resolver to do reflective lookups
-      // even before classes are initialized.
-      return;
+    Array<Method*>* methods = klass->methods();
+    for (int i = 0; i < methods->length(); i++) {
+      const methodHandle mh(THREAD, methods->at(i));
+      CompilationPolicy::maybe_compile_early_after_init(mh, THREAD);
     }
-    CompLevel level = initial_compile_level(m);
-    if (PrintTieredEvents) {
-      print_event(COMPILE, m(), m(), InvocationEntryBci, level);
+  }
+}
+
+void CompilationPolicy::replay_training_at_init(bool is_on_shutdown, TRAPS) {
+  // Drain pending queue when no concurrent processing thread is present.
+  if (UseConcurrentTrainingReplay) {
+    if (VerifyTrainingData) {
+      MonitorLocker locker(THREAD, TrainingReplayQueue_lock);
+      while (!_training_replay_queue.is_empty_unlocked()) {
+        locker.wait(); // let the replay training thread drain the queue
+      }
+    }
+  } else {
+    do {
+      InstanceKlass* pending = _training_replay_queue.try_pop(TrainingReplayQueue_lock, THREAD);
+      if (pending == nullptr) {
+        break; // drained the queue
+      }
+      if (is_on_shutdown) {
+        LogStreamHandle(Warning, training) log;
+        if (log.is_enabled()) {
+          ResourceMark rm;
+          log.print("pending training replay request: %s%s",
+                    pending->external_name(), (pending->has_aot_initialized_mirror() ? " (preinitialized)" : ""));
+        }
+      }
+      replay_training_at_init_impl(pending, THREAD);
+    } while (true);
+  }
+
+  if (VerifyTrainingData) {
+    TrainingData::verify();
+  }
+}
+
+void CompilationPolicy::replay_training_at_init(InstanceKlass* klass, TRAPS) {
+  assert(klass->is_initialized(), "");
+  if (TrainingData::have_data() && klass->is_shared() &&
+      (CompileBroker::replay_initialized() || !klass->has_aot_initialized_mirror())) { // ignore preloaded classes during early startup
+    if (UseConcurrentTrainingReplay || !CompileBroker::replay_initialized()) {
+      _training_replay_queue.push(klass, TrainingReplayQueue_lock, THREAD);
+    } else {
+      replay_training_at_init_impl(klass, THREAD);
     }
-    CompileBroker::compile_method(m, InvocationEntryBci, level, methodHandle(), 0, CompileTask::Reason_MustBeCompiled, THREAD);
+    assert(!HAS_PENDING_EXCEPTION, "");
+  }
+}
+
+// For TrainingReplayQueue
+template<>
+void CompilationPolicyUtils::Queue<InstanceKlass>::print_on(outputStream* st) {
+  int pos = 0;
+  for (QueueNode* cur = _head; cur != nullptr; cur = cur->next()) {
+    ResourceMark rm;
+    InstanceKlass* ik = cur->value();
+    st->print_cr("%3d: " INTPTR_FORMAT " %s", ++pos, p2i(ik), ik->external_name());
+  }
+}
+
+void CompilationPolicy::replay_training_at_init_loop(TRAPS) {
+  precond(UseConcurrentTrainingReplay);
+
+  while (!CompileBroker::is_compilation_disabled_forever() || VerifyTrainingData) {
+    InstanceKlass* ik = _training_replay_queue.pop(TrainingReplayQueue_lock, THREAD);
+    replay_training_at_init_impl(ik, THREAD);
   }
 }
 
 static inline CompLevel adjust_level_for_compilability_query(CompLevel comp_level) {
   if (comp_level == CompLevel_any) {
@@ -121,11 +263,11 @@
 }
 
 // Returns true if m is allowed to be compiled
 bool CompilationPolicy::can_be_compiled(const methodHandle& m, int comp_level) {
   // allow any levels for WhiteBox
-  assert(WhiteBoxAPI || comp_level == CompLevel_any || is_compile(comp_level), "illegal compilation level");
+  assert(WhiteBoxAPI || comp_level == CompLevel_any || is_compile(comp_level), "illegal compilation level %d", comp_level);
 
   if (m->is_abstract()) return false;
   if (DontCompileHugeMethods && m->code_size() > HugeMethodLimit) return false;
 
   // Math intrinsics should never be compiled as this can lead to
@@ -201,11 +343,11 @@
   if (CompilationModeFlag::quick_internal()) {
 #if INCLUDE_JVMCI
     if (UseJVMCICompiler) {
       AbstractCompiler* comp = CompileBroker::compiler(CompLevel_full_optimization);
       if (comp != nullptr && comp->is_jvmci() && ((JVMCICompiler*) comp)->force_comp_at_level_simple(method)) {
-        return true;
+        return !SCCache::is_C3_on();
       }
     }
 #endif
   }
   return false;
@@ -321,11 +463,11 @@
     return k;
   }
   return 1;
 }
 
-void CompilationPolicy::print_counters(const char* prefix, const Method* m) {
+void CompilationPolicy::print_counters(const char* prefix, Method* m) {
   int invocation_count = m->invocation_count();
   int backedge_count = m->backedge_count();
   MethodData* mdh = m->method_data();
   int mdo_invocations = 0, mdo_backedges = 0;
   int mdo_invocations_start = 0, mdo_backedges_start = 0;
@@ -341,12 +483,40 @@
       mdo_backedges, mdo_backedges_start);
   tty->print(" %smax levels=%d,%d", prefix,
       m->highest_comp_level(), m->highest_osr_comp_level());
 }
 
+void CompilationPolicy::print_training_data(const char* prefix, Method* method) {
+  methodHandle m(Thread::current(), method);
+  tty->print(" %smtd: ", prefix);
+  MethodTrainingData* mtd = MethodTrainingData::find(m);
+  if (mtd == nullptr) {
+    tty->print("null");
+  } else {
+    MethodData* md = mtd->final_profile();
+    tty->print("mdo=");
+    if (md == nullptr) {
+      tty->print("null");
+    } else {
+      int mdo_invocations = md->invocation_count();
+      int mdo_backedges = md->backedge_count();
+      int mdo_invocations_start = md->invocation_count_start();
+      int mdo_backedges_start = md->backedge_count_start();
+      tty->print("%d(%d), %d(%d)", mdo_invocations, mdo_invocations_start, mdo_backedges, mdo_backedges_start);
+    }
+    CompileTrainingData* ctd = mtd->last_toplevel_compile(CompLevel_full_optimization);
+    tty->print(", deps=");
+    if (ctd == nullptr) {
+      tty->print("null");
+    } else {
+      tty->print("%d", ctd->init_deps_left());
+    }
+  }
+}
+
 // Print an event.
-void CompilationPolicy::print_event(EventType type, const Method* m, const Method* im, int bci, CompLevel level) {
+void CompilationPolicy::print_event(EventType type, Method* m, Method* im, int bci, CompLevel level) {
   bool inlinee_event = m != im;
 
   ttyLocker tty_lock;
   tty->print("%lf: [", os::elapsedTime());
 
@@ -358,10 +528,16 @@
     tty->print("loop");
     break;
   case COMPILE:
     tty->print("compile");
     break;
+  case FORCE_COMPILE:
+    tty->print("force-compile");
+    break;
+  case FORCE_RECOMPILE:
+    tty->print("force-recompile");
+    break;
   case REMOVE_FROM_QUEUE:
     tty->print("remove-from-queue");
     break;
   case UPDATE_IN_QUEUE:
     tty->print("update-in-queue");
@@ -391,10 +567,12 @@
 
   tty->print(" rate=");
   if (m->prev_time() == 0) tty->print("n/a");
   else tty->print("%f", m->rate());
 
+  RecompilationPolicy::print_load_average();
+
   tty->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback),
                                threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback));
 
   if (type != COMPILE) {
     print_counters("", m);
@@ -423,10 +601,14 @@
     }
     tty->print(" status=");
     if (m->queued_for_compilation()) {
       tty->print("in-queue");
     } else tty->print("idle");
+    print_training_data("", m);
+    if (inlinee_event) {
+      print_training_data("inlinee ", im);
+    }
   }
   tty->print_cr("]");
 }
 
 void CompilationPolicy::initialize() {
@@ -486,24 +668,34 @@
       if (UseJVMCICompiler && UseJVMCINativeLibrary) {
         int libjvmci_count = MAX2((int) (count * JVMCINativeLibraryThreadFraction), 1);
         int c1_count = MAX2(count - libjvmci_count, 1);
         set_c2_count(libjvmci_count);
         set_c1_count(c1_count);
+      } else if (SCCache::is_C3_on()) {
+        set_c1_count(MAX2(count / 3, 1));
+        set_c2_count(MAX2(count - c1_count(), 1));
+        set_c3_count(1);
       } else
 #endif
       {
         set_c1_count(MAX2(count / 3, 1));
         set_c2_count(MAX2(count - c1_count(), 1));
       }
     }
+    if (SCCache::is_code_load_thread_on()) {
+      set_sc_count((c1_only || c2_only) ? 1 : 2); // At minimum we need 2 threads to load C1 and C2 cached code in parallel
+    }
     assert(count == c1_count() + c2_count(), "inconsistent compiler thread count");
     set_increase_threshold_at_ratio();
   }
+
   set_start_time(nanos_to_millis(os::javaTimeNanos()));
 }
 
 
+
+
 #ifdef ASSERT
 bool CompilationPolicy::verify_level(CompLevel level) {
   if (TieredCompilation && level > TieredStopAtLevel) {
     return false;
   }
@@ -616,39 +808,45 @@
     mdo->backedge_counter()->set_carry_on_overflow();
   }
 }
 
 // Called with the queue locked and with at least one element
-CompileTask* CompilationPolicy::select_task(CompileQueue* compile_queue) {
+CompileTask* CompilationPolicy::select_task(CompileQueue* compile_queue, JavaThread* THREAD) {
   CompileTask *max_blocking_task = nullptr;
   CompileTask *max_task = nullptr;
   Method* max_method = nullptr;
 
-  jlong t = nanos_to_millis(os::javaTimeNanos());
+  int64_t t = nanos_to_millis(os::javaTimeNanos());
   // Iterate through the queue and find a method with a maximum rate.
   for (CompileTask* task = compile_queue->first(); task != nullptr;) {
     CompileTask* next_task = task->next();
     // If a method was unloaded or has been stale for some time, remove it from the queue.
     // Blocking tasks and tasks submitted from whitebox API don't become stale
     if (task->is_unloaded()) {
       compile_queue->remove_and_mark_stale(task);
       task = next_task;
       continue;
     }
+    if (task->is_scc()) {
+      // SCC tasks are on separate queue, and they should load fast. There is no need to walk
+      // the rest of the queue, just take the task and go.
+      return task;
+    }
     Method* method = task->method();
-    methodHandle mh(Thread::current(), method);
+    methodHandle mh(THREAD, method);
     if (task->can_become_stale() && is_stale(t, TieredCompileTaskTimeout, mh) && !is_old(mh)) {
       if (PrintTieredEvents) {
         print_event(REMOVE_FROM_QUEUE, method, method, task->osr_bci(), (CompLevel) task->comp_level());
       }
       method->clear_queued_for_compilation();
+      method->set_pending_queue_processed(false);
       compile_queue->remove_and_mark_stale(task);
       task = next_task;
       continue;
     }
     update_rate(t, mh);
-    if (max_task == nullptr || compare_methods(method, max_method)) {
+    if (max_task == nullptr || compare_methods(method, max_method) || compare_tasks(task, max_task)) {
       // Select a method with the highest rate
       max_task = task;
       max_method = method;
     }
 
@@ -669,17 +867,19 @@
     // chance of such compilations timing out.
     max_task = max_blocking_task;
     max_method = max_task->method();
   }
 
-  methodHandle max_method_h(Thread::current(), max_method);
+  methodHandle max_method_h(THREAD, max_method);
 
   if (max_task != nullptr && max_task->comp_level() == CompLevel_full_profile && TieredStopAtLevel > CompLevel_full_profile &&
       max_method != nullptr && is_method_profiled(max_method_h) && !Arguments::is_compiler_only()) {
     max_task->set_comp_level(CompLevel_limited_profile);
 
-    if (CompileBroker::compilation_is_complete(max_method_h, max_task->osr_bci(), CompLevel_limited_profile)) {
+    if (CompileBroker::compilation_is_complete(max_method_h(), max_task->osr_bci(), CompLevel_limited_profile,
+                                               false /* requires_online_compilation */,
+                                               CompileTask::Reason_None)) {
       if (PrintTieredEvents) {
         print_event(REMOVE_FROM_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
       }
       compile_queue->remove_and_mark_stale(max_task);
       max_method->clear_queued_for_compilation();
@@ -688,11 +888,10 @@
 
     if (PrintTieredEvents) {
       print_event(UPDATE_IN_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
     }
   }
-
   return max_task;
 }
 
 void CompilationPolicy::reprofile(ScopeDesc* trap_scope, bool is_osr) {
   for (ScopeDesc* sd = trap_scope;; sd = sd->sender()) {
@@ -711,10 +910,17 @@
                                       int branch_bci, int bci, CompLevel comp_level, nmethod* nm, TRAPS) {
   if (PrintTieredEvents) {
     print_event(bci == InvocationEntryBci ? CALL : LOOP, method(), inlinee(), bci, comp_level);
   }
 
+#if INCLUDE_JVMCI
+  if (EnableJVMCI && UseJVMCICompiler &&
+      comp_level == CompLevel_full_optimization && !AOTLinkedClassBulkLoader::class_preloading_finished()) {
+    return nullptr;
+  }
+#endif
+
   if (comp_level == CompLevel_none &&
       JvmtiExport::can_post_interpreter_events() &&
       THREAD->is_interp_only_mode()) {
     return nullptr;
   }
@@ -806,16 +1012,26 @@
     if (PrintTieredEvents) {
       print_event(COMPILE, mh(), mh(), bci, level);
     }
     int hot_count = (bci == InvocationEntryBci) ? mh->invocation_count() : mh->backedge_count();
     update_rate(nanos_to_millis(os::javaTimeNanos()), mh);
-    CompileBroker::compile_method(mh, bci, level, mh, hot_count, CompileTask::Reason_Tiered, THREAD);
+    bool requires_online_compilation = false;
+    if (TrainingData::have_data()) {
+      MethodTrainingData* mtd = MethodTrainingData::find(mh);
+      if (mtd != nullptr) {
+        CompileTrainingData* ctd = mtd->last_toplevel_compile(level);
+        if (ctd != nullptr) {
+          requires_online_compilation = (ctd->init_deps_left() > 0);
+        }
+      }
+    }
+    CompileBroker::compile_method(mh, bci, level, mh, hot_count, requires_online_compilation, CompileTask::Reason_Tiered, THREAD);
   }
 }
 
 // update_rate() is called from select_task() while holding a compile queue lock.
-void CompilationPolicy::update_rate(jlong t, const methodHandle& method) {
+void CompilationPolicy::update_rate(int64_t t, const methodHandle& method) {
   // Skip update if counters are absent.
   // Can't allocate them since we are holding compile queue lock.
   if (method->method_counters() == nullptr)  return;
 
   if (is_old(method)) {
@@ -825,12 +1041,12 @@
     return;
   }
 
   // We don't update the rate if we've just came out of a safepoint.
   // delta_s is the time since last safepoint in milliseconds.
-  jlong delta_s = t - SafepointTracing::end_of_last_safepoint_ms();
-  jlong delta_t = t - (method->prev_time() != 0 ? method->prev_time() : start_time()); // milliseconds since the last measurement
+  int64_t delta_s = t - SafepointTracing::end_of_last_safepoint_ms();
+  int64_t delta_t = t - (method->prev_time() != 0 ? method->prev_time() : start_time()); // milliseconds since the last measurement
   // How many events were there since the last time?
   int event_count = method->invocation_count() + method->backedge_count();
   int delta_e = event_count - method->prev_event_count();
 
   // We should be running for at least 1ms.
@@ -849,13 +1065,13 @@
   }
 }
 
 // Check if this method has been stale for a given number of milliseconds.
 // See select_task().
-bool CompilationPolicy::is_stale(jlong t, jlong timeout, const methodHandle& method) {
-  jlong delta_s = t - SafepointTracing::end_of_last_safepoint_ms();
-  jlong delta_t = t - method->prev_time();
+bool CompilationPolicy::is_stale(int64_t t, int64_t timeout, const methodHandle& method) {
+  int64_t delta_s = t - SafepointTracing::end_of_last_safepoint_ms();
+  int64_t delta_t = t - method->prev_time();
   if (delta_t > timeout && delta_s > timeout) {
     int event_count = method->invocation_count() + method->backedge_count();
     int delta_e = event_count - method->prev_event_count();
     // Return true if there were no events.
     return delta_e == 0;
@@ -889,10 +1105,18 @@
       }
     }
   return false;
 }
 
+bool CompilationPolicy::compare_tasks(CompileTask* x, CompileTask* y) {
+  assert(!x->is_scc() && !y->is_scc(), "SC tasks are not expected here");
+  if (x->compile_reason() != y->compile_reason() && y->compile_reason() == CompileTask::Reason_MustBeCompiled) {
+    return true;
+  }
+  return false;
+}
+
 // Is method profiled enough?
 bool CompilationPolicy::is_method_profiled(const methodHandle& method) {
   MethodData* mdo = method->method_data();
   if (mdo != nullptr) {
     int i = mdo->invocation_count_delta();
@@ -902,17 +1126,16 @@
   return false;
 }
 
 
 // Determine is a method is mature.
-bool CompilationPolicy::is_mature(Method* method) {
+bool CompilationPolicy::is_mature(MethodData* mdo) {
   if (Arguments::is_compiler_only()) {
     // Always report profiles as immature with -Xcomp
     return false;
   }
-  methodHandle mh(Thread::current(), method);
-  MethodData* mdo = method->method_data();
+  methodHandle mh(Thread::current(), mdo->method());
   if (mdo != nullptr) {
     int i = mdo->invocation_count();
     int b = mdo->backedge_count();
     double k = ProfileMaturityPercentage / 100.0;
     return CallPredicate::apply_scaled(mh, CompLevel_full_profile, i, b, k) || LoopPredicate::apply_scaled(mh, CompLevel_full_profile, i, b, k);
@@ -925,13 +1148,23 @@
 // We also take the load on compilers into the account.
 bool CompilationPolicy::should_create_mdo(const methodHandle& method, CompLevel cur_level) {
   if (cur_level != CompLevel_none || force_comp_at_level_simple(method) || CompilationModeFlag::quick_only() || !ProfileInterpreter) {
     return false;
   }
+
+  if (TrainingData::have_data()) {
+    MethodTrainingData* mtd = MethodTrainingData::find(method);
+    if (mtd != nullptr && mtd->saw_level(CompLevel_full_optimization)) {
+      return true;
+    }
+    return false;
+  }
+
   if (is_old(method)) {
     return true;
   }
+
   int i = method->invocation_count();
   int b = method->backedge_count();
   double k = Tier0ProfilingStartPercentage / 100.0;
 
   // If the top level compiler is not keeping up, delay profiling.
@@ -961,11 +1194,11 @@
     return;
   }
   if (mh->method_data() == nullptr) {
     Method::build_profiling_method_data(mh, CHECK_AND_CLEAR);
   }
-  if (ProfileInterpreter) {
+  if (ProfileInterpreter && THREAD->has_last_Java_frame()) {
     MethodData* mdo = mh->method_data();
     if (mdo != nullptr) {
       frame last_frame = THREAD->last_frame();
       if (last_frame.is_interpreted_frame() && mh == last_frame.interpreter_frame_method()) {
         int bci = last_frame.interpreter_frame_bci();
@@ -974,11 +1207,140 @@
       }
     }
   }
 }
 
+CompLevel CompilationPolicy::trained_transition_from_none(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD) {
+  precond(mtd != nullptr);
+  precond(cur_level == CompLevel_none);
 
+  if (mtd->only_inlined() && !mtd->saw_level(CompLevel_full_optimization)) {
+    return CompLevel_none;
+  }
+
+  bool training_has_profile = (mtd->final_profile() != nullptr);
+  if (mtd->saw_level(CompLevel_full_optimization) && !training_has_profile) {
+    return CompLevel_full_profile;
+  }
+
+  CompLevel highest_training_level = static_cast<CompLevel>(mtd->highest_top_level());
+  switch (highest_training_level) {
+    case CompLevel_limited_profile:
+    case CompLevel_full_profile:
+      return CompLevel_limited_profile;
+    case CompLevel_simple:
+      return CompLevel_simple;
+    case CompLevel_none:
+      return CompLevel_none;
+    default:
+      break;
+  }
+
+  // Now handle the case of level 4.
+  assert(highest_training_level == CompLevel_full_optimization, "Unexpected compilation level: %d", highest_training_level);
+  if (!training_has_profile) {
+    // The method was a part of a level 4 compile, but don't have a stored profile,
+    // we need to profile it.
+    return CompLevel_full_profile;
+  }
+  const bool deopt = (static_cast<CompLevel>(method->highest_comp_level()) == CompLevel_full_optimization);
+  // If we deopted, then we reprofile
+  if (deopt && !is_method_profiled(method)) {
+    return CompLevel_full_profile;
+  }
+
+  CompileTrainingData* ctd = mtd->last_toplevel_compile(CompLevel_full_optimization);
+  assert(ctd != nullptr, "Should have CTD for CompLevel_full_optimization");
+  // With SkipTier2IfPossible and all deps satisfied, go to level 4 immediately
+  if (SkipTier2IfPossible && ctd->init_deps_left() == 0) {
+    if (method->method_data() == nullptr) {
+      create_mdo(method, THREAD);
+    }
+    return CompLevel_full_optimization;
+  }
+
+  // Otherwise go to level 2
+  return CompLevel_limited_profile;
+}
+
+
+CompLevel CompilationPolicy::trained_transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD) {
+  precond(mtd != nullptr);
+  precond(cur_level == CompLevel_limited_profile);
+
+  // One of the main reasons that we can get here is that we're waiting for the stored C2 code to become ready.
+
+  // But first, check if we have a saved profile
+  bool training_has_profile = (mtd->final_profile() != nullptr);
+  if (!training_has_profile) {
+    return CompLevel_full_profile;
+  }
+
+
+  assert(training_has_profile, "Have to have a profile to be here");
+  // Check if the method is ready
+  CompileTrainingData* ctd = mtd->last_toplevel_compile(CompLevel_full_optimization);
+  if (ctd != nullptr && ctd->init_deps_left() == 0) {
+    if (method->method_data() == nullptr) {
+      create_mdo(method, THREAD);
+    }
+    return CompLevel_full_optimization;
+  }
+
+  // Otherwise stay at the current level
+  return CompLevel_limited_profile;
+}
+
+
+CompLevel CompilationPolicy::trained_transition_from_full_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD) {
+  precond(mtd != nullptr);
+  precond(cur_level == CompLevel_full_profile);
+
+  CompLevel highest_training_level = static_cast<CompLevel>(mtd->highest_top_level());
+  // We have method at the full profile level and we also know that it's possibly an important method.
+  if (highest_training_level == CompLevel_full_optimization && !mtd->only_inlined()) {
+    // Check if it is adequately profiled
+    if (is_method_profiled(method)) {
+      return CompLevel_full_optimization;
+    }
+  }
+
+  // Otherwise stay at the current level
+  return CompLevel_full_profile;
+}
+
+CompLevel CompilationPolicy::trained_transition(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD) {
+  precond(MethodTrainingData::have_data());
+
+  // If there is no training data recorded for this method, bail out.
+  if (mtd == nullptr) {
+    return cur_level;
+  }
+
+  CompLevel next_level = cur_level;
+  switch(cur_level) {
+    default: break;
+    case CompLevel_none:
+      next_level = trained_transition_from_none(method, cur_level, mtd, THREAD);
+      break;
+    case CompLevel_limited_profile:
+      next_level = trained_transition_from_limited_profile(method, cur_level, mtd, THREAD);
+      break;
+    case CompLevel_full_profile:
+      next_level = trained_transition_from_full_profile(method, cur_level, mtd, THREAD);
+      break;
+  }
+
+  // We don't have any special strategies for the C2-only compilation modes, so just fix up the levels for now.
+  if (CompilationModeFlag::high_only_quick_internal() && CompLevel_simple < next_level && next_level < CompLevel_full_optimization) {
+    return CompLevel_none;
+  }
+  if (CompilationModeFlag::high_only() && next_level < CompLevel_full_optimization) {
+    return CompLevel_none;
+  }
+  return (cur_level != next_level) ? limit_level(next_level) : cur_level;
+}
 
 /*
  * Method states:
  *   0 - interpreter (CompLevel_none)
  *   1 - pure C1 (CompLevel_simple)
@@ -1016,97 +1378,144 @@
  *
  */
 
 // Common transition function. Given a predicate determines if a method should transition to another level.
 template<typename Predicate>
-CompLevel CompilationPolicy::common(const methodHandle& method, CompLevel cur_level, bool disable_feedback) {
+CompLevel CompilationPolicy::common(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD, bool disable_feedback) {
   CompLevel next_level = cur_level;
   int i = method->invocation_count();
   int b = method->backedge_count();
 
   if (force_comp_at_level_simple(method)) {
     next_level = CompLevel_simple;
   } else {
-    if (is_trivial(method) || method->is_native()) {
-      next_level = CompilationModeFlag::disable_intermediate() ? CompLevel_full_optimization : CompLevel_simple;
-    } else {
-      switch(cur_level) {
-      default: break;
-      case CompLevel_none:
-        // If we were at full profile level, would we switch to full opt?
-        if (common<Predicate>(method, CompLevel_full_profile, disable_feedback) == CompLevel_full_optimization) {
-          next_level = CompLevel_full_optimization;
-        } else if (!CompilationModeFlag::disable_intermediate() && Predicate::apply(method, cur_level, i, b)) {
-          // C1-generated fully profiled code is about 30% slower than the limited profile
-          // code that has only invocation and backedge counters. The observation is that
-          // if C2 queue is large enough we can spend too much time in the fully profiled code
-          // while waiting for C2 to pick the method from the queue. To alleviate this problem
-          // we introduce a feedback on the C2 queue size. If the C2 queue is sufficiently long
-          // we choose to compile a limited profiled version and then recompile with full profiling
-          // when the load on C2 goes down.
-          if (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) >
-              Tier3DelayOn * compiler_count(CompLevel_full_optimization)) {
-            next_level = CompLevel_limited_profile;
-          } else {
-            next_level = CompLevel_full_profile;
-          }
-        }
-        break;
-      case CompLevel_limited_profile:
-        if (is_method_profiled(method)) {
-          // Special case: we got here because this method was fully profiled in the interpreter.
-          next_level = CompLevel_full_optimization;
-        } else {
-          MethodData* mdo = method->method_data();
-          if (mdo != nullptr) {
-            if (mdo->would_profile()) {
-              if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
-                                       Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
-                                       Predicate::apply(method, cur_level, i, b))) {
-                next_level = CompLevel_full_profile;
-              }
-            } else {
-              next_level = CompLevel_full_optimization;
-            }
-          } else {
-            // If there is no MDO we need to profile
-            if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
-                                     Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
-                                     Predicate::apply(method, cur_level, i, b))) {
-              next_level = CompLevel_full_profile;
-            }
-          }
-        }
-        break;
-      case CompLevel_full_profile:
-        {
-          MethodData* mdo = method->method_data();
-          if (mdo != nullptr) {
-            if (mdo->would_profile() || CompilationModeFlag::disable_intermediate()) {
-              int mdo_i = mdo->invocation_count_delta();
-              int mdo_b = mdo->backedge_count_delta();
-              if (Predicate::apply(method, cur_level, mdo_i, mdo_b)) {
-                next_level = CompLevel_full_optimization;
-              }
-            } else {
-              next_level = CompLevel_full_optimization;
-            }
-          }
+    if (MethodTrainingData::have_data()) {
+      MethodTrainingData* mtd = MethodTrainingData::find(method);
+      if (mtd == nullptr) {
+        // We haven't see compilations of this method in training. It's either very cold or the behavior changed.
+        // Feed it to the standard TF with no profiling delay.
+        next_level = standard_transition<Predicate>(method, cur_level, false /*delay_profiling*/, disable_feedback);
+      } else {
+        next_level = trained_transition(method, cur_level, mtd, THREAD);
+        if (cur_level == next_level) {
+          // trained_transtion() is going to return the same level if no startup/warmup optimizations apply.
+          // In order to catch possible pathologies due to behavior change we feed the event to the regular
+          // TF but with profiling delay.
+          next_level = standard_transition<Predicate>(method, cur_level, true /*delay_profiling*/, disable_feedback);
         }
-        break;
       }
+    } else if (is_trivial(method) || method->is_native()) {
+      next_level = CompilationModeFlag::disable_intermediate() ? CompLevel_full_optimization : CompLevel_simple;
+    } else {
+      next_level = standard_transition<Predicate>(method, cur_level, false /*delay_profiling*/, disable_feedback);
     }
   }
   return (next_level != cur_level) ? limit_level(next_level) : next_level;
 }
 
 
+template<typename Predicate>
+CompLevel CompilationPolicy::standard_transition(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) {
+  CompLevel next_level = cur_level;
+  switch(cur_level) {
+  default: break;
+  case CompLevel_none:
+    next_level = transition_from_none<Predicate>(method, cur_level, delay_profiling, disable_feedback);
+    break;
+  case CompLevel_limited_profile:
+    next_level = transition_from_limited_profile<Predicate>(method, cur_level, delay_profiling, disable_feedback);
+    break;
+  case CompLevel_full_profile:
+    next_level = transition_from_full_profile<Predicate>(method, cur_level);
+    break;
+  }
+  return next_level;
+}
+
+template<typename Predicate>
+CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) {
+  precond(cur_level == CompLevel_none);
+  CompLevel next_level = cur_level;
+  int i = method->invocation_count();
+  int b = method->backedge_count();
+  double scale = delay_profiling ? Tier0ProfileDelayFactor : 1.0;
+  // If we were at full profile level, would we switch to full opt?
+  if (transition_from_full_profile<Predicate>(method, CompLevel_full_profile) == CompLevel_full_optimization) {
+    next_level = CompLevel_full_optimization;
+  } else if (!CompilationModeFlag::disable_intermediate() && Predicate::apply_scaled(method, cur_level, i, b, scale)) {
+    // C1-generated fully profiled code is about 30% slower than the limited profile
+    // code that has only invocation and backedge counters. The observation is that
+    // if C2 queue is large enough we can spend too much time in the fully profiled code
+    // while waiting for C2 to pick the method from the queue. To alleviate this problem
+    // we introduce a feedback on the C2 queue size. If the C2 queue is sufficiently long
+    // we choose to compile a limited profiled version and then recompile with full profiling
+    // when the load on C2 goes down.
+    if (delay_profiling || (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) > Tier3DelayOn * compiler_count(CompLevel_full_optimization))) {
+      next_level = CompLevel_limited_profile;
+    } else {
+      next_level = CompLevel_full_profile;
+    }
+  }
+  return next_level;
+}
+
+template<typename Predicate>
+CompLevel CompilationPolicy::transition_from_full_profile(const methodHandle& method, CompLevel cur_level) {
+  precond(cur_level == CompLevel_full_profile);
+  CompLevel next_level = cur_level;
+  MethodData* mdo = method->method_data();
+  if (mdo != nullptr) {
+    if (mdo->would_profile() || CompilationModeFlag::disable_intermediate()) {
+      int mdo_i = mdo->invocation_count_delta();
+      int mdo_b = mdo->backedge_count_delta();
+      if (Predicate::apply(method, cur_level, mdo_i, mdo_b)) {
+        next_level = CompLevel_full_optimization;
+      }
+    } else {
+      next_level = CompLevel_full_optimization;
+    }
+  }
+  return next_level;
+}
+
+template<typename Predicate>
+CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) {
+  precond(cur_level == CompLevel_limited_profile);
+  CompLevel next_level = cur_level;
+  int i = method->invocation_count();
+  int b = method->backedge_count();
+  double scale = delay_profiling ? Tier2ProfileDelayFactor : 1.0;
+  MethodData* mdo = method->method_data();
+  if (mdo != nullptr) {
+    if (mdo->would_profile()) {
+      if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
+                              Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
+                              Predicate::apply_scaled(method, cur_level, i, b, scale))) {
+        next_level = CompLevel_full_profile;
+      }
+    } else {
+      next_level = CompLevel_full_optimization;
+    }
+  } else {
+    // If there is no MDO we need to profile
+    if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
+                            Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
+                            Predicate::apply_scaled(method, cur_level, i, b, scale))) {
+      next_level = CompLevel_full_profile;
+    }
+  }
+  if (next_level == CompLevel_full_profile && is_method_profiled(method)) {
+    next_level = CompLevel_full_optimization;
+  }
+  return next_level;
+}
+
 
 // Determine if a method should be compiled with a normal entry point at a different level.
-CompLevel CompilationPolicy::call_event(const methodHandle& method, CompLevel cur_level, Thread* thread) {
-  CompLevel osr_level = MIN2((CompLevel) method->highest_osr_comp_level(), common<LoopPredicate>(method, cur_level, true));
-  CompLevel next_level = common<CallPredicate>(method, cur_level, is_old(method));
+CompLevel CompilationPolicy::call_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD) {
+  CompLevel osr_level = MIN2((CompLevel) method->highest_osr_comp_level(), common<LoopPredicate>(method, cur_level, THREAD, true));
+  CompLevel next_level = common<CallPredicate>(method, cur_level, THREAD, !TrainingData::have_data() && is_old(method));
 
   // If OSR method level is greater than the regular method level, the levels should be
   // equalized by raising the regular method level in order to avoid OSRs during each
   // invocation of the method.
   if (osr_level == CompLevel_full_optimization && cur_level == CompLevel_full_profile) {
@@ -1116,16 +1525,22 @@
       next_level = CompLevel_full_optimization;
     }
   } else {
     next_level = MAX2(osr_level, next_level);
   }
+#if INCLUDE_JVMCI
+  if (EnableJVMCI && UseJVMCICompiler &&
+      next_level == CompLevel_full_optimization && !AOTLinkedClassBulkLoader::class_preloading_finished()) {
+    next_level = cur_level;
+  }
+#endif
   return next_level;
 }
 
 // Determine if we should do an OSR compilation of a given method.
-CompLevel CompilationPolicy::loop_event(const methodHandle& method, CompLevel cur_level, Thread* thread) {
-  CompLevel next_level = common<LoopPredicate>(method, cur_level, true);
+CompLevel CompilationPolicy::loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD) {
+  CompLevel next_level = common<LoopPredicate>(method, cur_level, THREAD, true);
   if (cur_level == CompLevel_none) {
     // If there is a live OSR method that means that we deopted to the interpreter
     // for the transition.
     CompLevel osr_level = MIN2((CompLevel)method->highest_osr_comp_level(), next_level);
     if (osr_level > CompLevel_none) {
diff a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp
--- a/src/hotspot/share/compiler/compilationPolicy.hpp
+++ b/src/hotspot/share/compiler/compilationPolicy.hpp
@@ -26,12 +26,87 @@
 #define SHARE_COMPILER_COMPILATIONPOLICY_HPP
 
 #include "code/nmethod.hpp"
 #include "compiler/compileBroker.hpp"
 #include "oops/methodData.hpp"
+#include "oops/trainingData.hpp"
 #include "utilities/globalDefinitions.hpp"
 
+namespace CompilationPolicyUtils {
+template<typename T>
+class Queue {
+  class QueueNode : public CHeapObj<mtCompiler> {
+    T* _value;
+    QueueNode* _next;
+  public:
+    QueueNode(T* value, QueueNode* next) : _value(value), _next(next) { }
+    T* value() const { return _value; }
+    void set_next(QueueNode* next) { _next = next; }
+    QueueNode* next() const { return _next; }
+  };
+
+  QueueNode* _head;
+  QueueNode* _tail;
+
+  void push_unlocked(T* value) {
+    QueueNode* n = new QueueNode(value, nullptr);
+    if (_tail != nullptr) {
+      _tail->set_next(n);
+    }
+    _tail = n;
+    if (_head == nullptr) {
+      _head = _tail;
+    }
+  }
+  T* pop_unlocked() {
+    QueueNode* n = _head;
+    if (_head != nullptr) {
+      _head = _head->next();
+    }
+    if (_head == nullptr) {
+      _tail = _head;
+    }
+    T* value = nullptr;
+    if (n != nullptr) {
+      value = n->value();
+      delete n;
+    }
+    return value;
+  }
+public:
+  Queue() : _head(nullptr), _tail(nullptr) { }
+  void push(T* value, Monitor* lock, TRAPS) {
+    MonitorLocker locker(THREAD, lock);
+    push_unlocked(value);
+    locker.notify_all();
+  }
+
+  bool is_empty_unlocked() const { return _head == nullptr; }
+
+  T* pop(Monitor* lock, TRAPS) {
+    MonitorLocker locker(THREAD, lock);
+    while(is_empty_unlocked() && !CompileBroker::is_compilation_disabled_forever()) {
+      locker.notify_all(); // notify that queue is empty
+      locker.wait();
+    }
+    T* value = pop_unlocked();
+    return value;
+  }
+
+  T* try_pop(Monitor* lock, TRAPS) {
+    MonitorLocker locker(THREAD, lock);
+    T* value = nullptr;
+    if (!is_empty_unlocked()) {
+      value = pop_unlocked();
+    }
+    return value;
+  }
+
+  void print_on(outputStream* st);
+};
+} // namespace CompilationPolicyUtils
+
 class CompileTask;
 class CompileQueue;
 /*
  *  The system supports 5 execution levels:
  *  * level 0 - interpreter (Profiling is tracked by a MethodData object, or MDO in short)
@@ -170,14 +245,18 @@
  */
 
 class CompilationPolicy : AllStatic {
   friend class CallPredicate;
   friend class LoopPredicate;
+  friend class RecompilationPolicy;
 
-  static jlong _start_time;
-  static int _c1_count, _c2_count;
+  typedef CompilationPolicyUtils::Queue<InstanceKlass> TrainingReplayQueue;
+
+  static int64_t _start_time;
+  static int _c1_count, _c2_count, _c3_count, _sc_count;
   static double _increase_threshold_at_ratio;
+  static TrainingReplayQueue _training_replay_queue;
 
   // Set carry flags in the counters (in Method* and MDO).
   inline static void handle_counter_overflow(const methodHandle& method);
 #ifdef ASSERT
   // Verify that a level is consistent with the compilation mode
@@ -185,33 +264,50 @@
 #endif
   // Clamp the request level according to various constraints.
   inline static CompLevel limit_level(CompLevel level);
   // Common transition function. Given a predicate determines if a method should transition to another level.
   template<typename Predicate>
-  static CompLevel common(const methodHandle& method, CompLevel cur_level, bool disable_feedback = false);
+  static CompLevel common(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD, bool disable_feedback = false);
+
+  template<typename Predicate>
+  static CompLevel transition_from_none(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback);
+  template<typename Predicate>
+  static CompLevel transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback);
+  template<typename Predicate>
+  static CompLevel transition_from_full_profile(const methodHandle& method, CompLevel cur_level);
+  template<typename Predicate>
+  static CompLevel standard_transition(const methodHandle& method, CompLevel cur_level, bool delayprof, bool disable_feedback);
+
+  static CompLevel trained_transition_from_none(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD);
+  static CompLevel trained_transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD);
+  static CompLevel trained_transition_from_full_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD);
+  static CompLevel trained_transition(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD);
+
   // Transition functions.
   // call_event determines if a method should be compiled at a different
   // level with a regular invocation entry.
-  static CompLevel call_event(const methodHandle& method, CompLevel cur_level, Thread* thread);
+  static CompLevel call_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD);
   // loop_event checks if a method should be OSR compiled at a different
   // level.
-  static CompLevel loop_event(const methodHandle& method, CompLevel cur_level, Thread* thread);
-  static void print_counters(const char* prefix, const Method* m);
+  static CompLevel loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD);
+  static void print_counters(const char* prefix, Method* m);
+  static void print_training_data(const char* prefix, Method* method);
   // Has a method been long around?
   // We don't remove old methods from the compile queue even if they have
   // very low activity (see select_task()).
   inline static bool is_old(const methodHandle& method);
   // Was a given method inactive for a given number of milliseconds.
   // If it is, we would remove it from the queue (see select_task()).
-  inline static bool is_stale(jlong t, jlong timeout, const methodHandle& method);
+  inline static bool is_stale(int64_t t, int64_t timeout, const methodHandle& method);
   // Compute the weight of the method for the compilation scheduling
   inline static double weight(Method* method);
   // Apply heuristics and return true if x should be compiled before y
   inline static bool compare_methods(Method* x, Method* y);
+  inline static bool compare_tasks(CompileTask* x, CompileTask* y);
   // Compute event rate for a given method. The rate is the number of event (invocations + backedges)
   // per millisecond.
-  inline static void update_rate(jlong t, const methodHandle& method);
+  inline static void update_rate(int64_t t, const methodHandle& method);
   // Compute threshold scaling coefficient
   inline static double threshold_scale(CompLevel level, int feedback_k);
   // If a method is old enough and is still in the interpreter we would want to
   // start profiling without waiting for the compiled method to arrive. This function
   // determines whether we should do that.
@@ -221,13 +317,15 @@
   // Is method profiled enough?
   static bool is_method_profiled(const methodHandle& method);
 
   static void set_c1_count(int x) { _c1_count = x;    }
   static void set_c2_count(int x) { _c2_count = x;    }
+  static void set_c3_count(int x) { _c3_count = x;    }
+  static void set_sc_count(int x) { _sc_count = x;    }
 
-  enum EventType { CALL, LOOP, COMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT };
-  static void print_event(EventType type, const Method* m, const Method* im, int bci, CompLevel level);
+  enum EventType { CALL, LOOP, COMPILE, FORCE_COMPILE, FORCE_RECOMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT };
+  static void print_event(EventType type, Method* m, Method* im, int bci, CompLevel level);
   // Check if the method can be compiled, change level if necessary
   static void compile(const methodHandle& mh, int bci, CompLevel level, TRAPS);
   // Simple methods are as good being compiled with C1 as C2.
   // This function tells if it's such a function.
   inline static bool is_trivial(const methodHandle& method);
@@ -240,25 +338,33 @@
                                       CompLevel level, nmethod* nm, TRAPS);
   static void method_back_branch_event(const methodHandle& method, const methodHandle& inlinee,
                                       int bci, CompLevel level, nmethod* nm, TRAPS);
 
   static void set_increase_threshold_at_ratio() { _increase_threshold_at_ratio = 100 / (100 - (double)IncreaseFirstTierCompileThresholdAt); }
-  static void set_start_time(jlong t) { _start_time = t;    }
-  static jlong start_time()           { return _start_time; }
+  static void set_start_time(int64_t t) { _start_time = t;    }
+  static int64_t start_time()           { return _start_time; }
 
   // m must be compiled before executing it
   static bool must_be_compiled(const methodHandle& m, int comp_level = CompLevel_any);
-public:
+  static void maybe_compile_early(const methodHandle& m, TRAPS);
+  static void maybe_compile_early_after_init(const methodHandle& m, TRAPS);
+  static void replay_training_at_init_impl(InstanceKlass* klass, TRAPS);
+ public:
   static int min_invocations() { return Tier4MinInvocationThreshold; }
   static int c1_count() { return _c1_count; }
   static int c2_count() { return _c2_count; }
+  static int c3_count() { return _c3_count; }
+  static int sc_count() { return _sc_count; }
   static int compiler_count(CompLevel comp_level);
-
   // If m must_be_compiled then request a compilation from the CompileBroker.
   // This supports the -Xcomp option.
   static void compile_if_required(const methodHandle& m, TRAPS);
 
+  static void replay_training_at_init(bool is_on_shutdown, TRAPS);
+  static void replay_training_at_init(InstanceKlass* klass, TRAPS);
+  static void replay_training_at_init_loop(TRAPS);
+
   // m is allowed to be compiled
   static bool can_be_compiled(const methodHandle& m, int comp_level = CompLevel_any);
   // m is allowed to be osr compiled
   static bool can_be_osr_compiled(const methodHandle& m, int comp_level = CompLevel_any);
   static bool is_compilation_enabled();
@@ -267,19 +373,24 @@
   // Return initial compile level to use with Xcomp (depends on compilation mode).
   static void reprofile(ScopeDesc* trap_scope, bool is_osr);
   static nmethod* event(const methodHandle& method, const methodHandle& inlinee,
                         int branch_bci, int bci, CompLevel comp_level, nmethod* nm, TRAPS);
   // Select task is called by CompileBroker. We should return a task or nullptr.
-  static CompileTask* select_task(CompileQueue* compile_queue);
+  static CompileTask* select_task(CompileQueue* compile_queue, JavaThread* THREAD);
   // Tell the runtime if we think a given method is adequately profiled.
-  static bool is_mature(Method* method);
+  static bool is_mature(MethodData* mdo);
   // Initialize: set compiler thread count
   static void initialize();
   static bool should_not_inline(ciEnv* env, ciMethod* callee);
 
   // Return desired initial compilation level for Xcomp
   static CompLevel initial_compile_level(const methodHandle& method);
   // Return highest level possible
   static CompLevel highest_compile_level();
+  static void dump();
+
+  static void sample_load_average();
+  static bool have_recompilation_work();
+  static bool recompilation_step(int step, TRAPS);
 };
 
 #endif // SHARE_COMPILER_COMPILATIONPOLICY_HPP
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,25 +21,30 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotLinkedClassBulkLoader.hpp"
+#include "cds/cdsConfig.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 "compiler/recompilationPolicy.hpp"
 #include "gc/shared/memAllocator.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "jvm.h"
 #include "jfr/jfrEvents.hpp"
 #include "logging/log.hpp"
@@ -65,18 +70,20 @@
 #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"
+#include "utilities/nonblockingQueue.inline.hpp"
 #ifdef COMPILER1
 #include "c1/c1_Compiler.hpp"
 #endif
 #ifdef COMPILER2
 #include "opto/c2compiler.hpp"
@@ -120,27 +127,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 +190,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 +205,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 +243,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 +287,12 @@
  */
 bool CompileBroker::can_remove(CompilerThread *ct, bool do_it) {
   assert(UseDynamicNumberOfCompilerThreads, "or shouldn't be here");
   if (!ReduceNumberOfCompilerThreads) return false;
 
+  if (RecompilationPolicy::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 +335,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 +361,87 @@
   }
 
   // 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) {
+      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();
+  }
+}
+
+static bool process_pending(CompileTask* task) {
+//  guarantee(task->method()->queued_for_compilation(), "");
+  if (task->is_unloaded()) {
+    return true; // unloaded
+  }
+  task->method()->set_queued_for_compilation(); // FIXME
+  if (task->method()->pending_queue_processed()) {
+    return true; // already queued
+  }
+  // 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())) {
+    return true; // already compiled
+  }
+  return false; // active
+}
+
+void CompileQueue::transfer_pending() {
+  assert(_lock->owned_by_self(), "must own lock");
+  while (!_queue.empty()) {
+    CompileTask* task = _queue.pop();
+    bool is_stale = process_pending(task);
+    if (is_stale) {
+      task->set_next(_first_stale);
+      task->set_prev(nullptr);
+      _first_stale = task;
+    } else {
+      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 +456,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();
+
+  RecompilationPolicy::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 +501,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 (RecompilationPolicy::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 +518,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 +540,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 +583,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);
@@ -506,21 +595,23 @@
 }
 
 // methods in the compile queue need to be marked as used on the stack
 // so that they don't get reclaimed by Redefine Classes
 void CompileQueue::mark_on_stack() {
-  CompileTask* task = _first;
-  while (task != nullptr) {
+  for (CompileTask* task = _first; task != nullptr; task = task->next()) {
+    task->mark_on_stack();
+  }
+  for (CompileTask* task = _queue.first(); !_queue.is_end(task); task = task->next()) {
+    assert(task != nullptr, "");
     task->mark_on_stack();
-    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  && (_sc_count > 0)) ? _sc2_compile_queue : _c2_compile_queue);
+  if (is_c1_compile(comp_level)) return ((is_scc && (_sc_count > 0)) ? _sc1_compile_queue : _c1_compile_queue);
   return nullptr;
 }
 
 CompileQueue* CompileBroker::c1_compile_queue() {
   return _c1_compile_queue;
@@ -542,14 +633,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 +712,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 +736,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 +875,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 +979,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 +1061,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 +1133,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 +1195,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) {
 
   int old_c2_count = 0, new_c2_count = 0, old_c1_count = 0, new_c1_count = 0;
   const int c2_tasks_per_thread = 2, c1_tasks_per_thread = 4;
 
@@ -1141,16 +1330,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
 //
@@ -1159,17 +1357,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);
@@ -1189,11 +1390,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)) {
@@ -1218,19 +1419,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)) {
@@ -1238,11 +1452,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
@@ -1328,29 +1542,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 (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 && !AOTLinkedClassBulkLoader::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()) {
@@ -1359,29 +1609,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;
@@ -1389,11 +1647,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;
@@ -1406,11 +1664,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);
@@ -1485,12 +1743,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) {
@@ -1502,13 +1763,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 {
@@ -1518,12 +1785,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;
     }
   }
 }
 
 
@@ -1619,11 +1895,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
@@ -1635,17 +1910,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
@@ -1782,10 +2058,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();
@@ -1819,10 +2099,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());
@@ -1838,10 +2120,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
@@ -1855,17 +2141,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++) {
@@ -1939,11 +2231,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);
 
+    RecompilationPolicy::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)) {
@@ -1977,10 +2272,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");
       }
@@ -2308,17 +2604,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);
@@ -2328,11 +2628,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");
@@ -2368,20 +2668,22 @@
         FormatBufferResource("COMPILE SKIPPED: %s",      failure_reason);
       task->print(tty, msg);
     }
   }
 
+  task->mark_finished(os::elapsed_counter());
+
   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());
   }
@@ -2416,10 +2718,16 @@
   // 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);
+
+  if (PrintCompilation) {
+    ResourceMark rm;
+    task->print_tty();
+  }
 }
 
 /**
  * The CodeCache is full. Print warning and disable compilation.
  * Schedule code cache cleaning so compilation can continue later.
@@ -2528,48 +2836,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);
@@ -2579,21 +2918,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) {
@@ -2652,32 +2991,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();
@@ -2686,10 +3154,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;
   }
@@ -2727,20 +3202,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();
diff a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp
--- a/src/hotspot/share/compiler/compileBroker.hpp
+++ b/src/hotspot/share/compiler/compileBroker.hpp
@@ -29,11 +29,13 @@
 #include "compiler/abstractCompiler.hpp"
 #include "compiler/compileTask.hpp"
 #include "compiler/compilerDirectives.hpp"
 #include "compiler/compilerThread.hpp"
 #include "runtime/atomic.hpp"
+#include "runtime/javaThread.hpp"
 #include "runtime/perfDataTypes.hpp"
+#include "utilities/nonblockingQueue.inline.hpp"
 #include "utilities/stack.hpp"
 #if INCLUDE_JVMCI
 #include "jvmci/jvmciCompiler.hpp"
 #endif
 
@@ -80,24 +82,29 @@
 // A list of CompileTasks.
 class CompileQueue : public CHeapObj<mtCompiler> {
  private:
   const char* _name;
 
+  NonblockingQueue<CompileTask, &CompileTask::next_ptr> _queue;
+
   CompileTask* _first;
   CompileTask* _last;
 
   CompileTask* _first_stale;
 
+  Monitor* _lock;
+
   volatile int _size;
   int _peak_size;
   uint _total_added;
   uint _total_removed;
 
   void purge_stale_tasks();
  public:
-  CompileQueue(const char* name) {
+  CompileQueue(const char* name, Monitor* lock) {
     _name = name;
+    _lock = lock;
     _first = nullptr;
     _last = nullptr;
     _size = 0;
     _total_added = 0;
     _total_removed = 0;
@@ -105,10 +112,13 @@
     _first_stale = nullptr;
   }
 
   const char*  name() const                      { return _name; }
 
+  void         add_pending(CompileTask* task);
+  void         transfer_pending();
+
   void         add(CompileTask* task);
   void         remove(CompileTask* task);
   void         remove_and_mark_stale(CompileTask* task);
   CompileTask* first()                           { return _first; }
   CompileTask* last()                            { return _last;  }
@@ -116,10 +126,12 @@
   CompileTask* get(CompilerThread* thread);
 
   bool         is_empty() const                  { return _first == nullptr; }
   int          size()     const                  { return _size;          }
 
+  Monitor* lock() const { return _lock; }
+
   int         get_peak_size()     const          { return _peak_size; }
   uint        get_total_added()   const          { return _total_added; }
   uint        get_total_removed() const          { return _total_removed; }
 
   // Redefine Classes support
@@ -160,34 +172,38 @@
   static int assign_compile_id (const methodHandle& method, int osr_bci);
 
 
  private:
   static bool _initialized;
+  static bool _replay_initialized;
   static volatile bool _should_block;
 
   // This flag can be used to stop compilation or turn it back on
   static volatile jint _should_compile_new_jobs;
 
   // The installed compiler(s)
-  static AbstractCompiler* _compilers[2];
+  static AbstractCompiler* _compilers[3];
 
   // The maximum numbers of compiler threads to be determined during startup.
-  static int _c1_count, _c2_count;
+  static int _c1_count, _c2_count, _c3_count, _sc_count;
 
   // An array of compiler thread Java objects
-  static jobject *_compiler1_objects, *_compiler2_objects;
+  static jobject *_compiler1_objects, *_compiler2_objects, *_compiler3_objects, *_sc_objects;
 
   // An array of compiler logs
-  static CompileLog **_compiler1_logs, **_compiler2_logs;
+  static CompileLog **_compiler1_logs, **_compiler2_logs, **_compiler3_logs, **_sc_logs;
 
   // These counters are used for assigning id's to each compilation
   static volatile jint _compilation_id;
   static volatile jint _osr_compilation_id;
   static volatile jint _native_compilation_id;
 
+  static CompileQueue* _c3_compile_queue;
   static CompileQueue* _c2_compile_queue;
   static CompileQueue* _c1_compile_queue;
+  static CompileQueue* _sc1_compile_queue;
+  static CompileQueue* _sc2_compile_queue;
 
   // performance counters
   static PerfCounter* _perf_total_compilation;
   static PerfCounter* _perf_osr_compilation;
   static PerfCounter* _perf_standard_compilation;
@@ -219,10 +235,11 @@
   static elapsedTimer _t_bailedout_compilation;
 
   static uint _total_compile_count;
   static uint _total_bailout_count;
   static uint _total_invalidated_count;
+  static uint _total_not_entrant_count;
   static uint _total_native_compile_count;
   static uint _total_osr_compile_count;
   static uint _total_standard_compile_count;
   static uint _total_compiler_stopped_count;
   static uint _total_compiler_restarted_count;
@@ -231,31 +248,38 @@
   static uint _sum_nmethod_size;
   static uint _sum_nmethod_code_size;
   static jlong _peak_compilation_time;
 
   static CompilerStatistics _stats_per_level[];
+  static CompilerStatistics _scc_stats;
+  static CompilerStatistics _scc_stats_per_level[];
 
   static volatile int _print_compilation_warning;
 
   enum ThreadType {
     compiler_t,
-    deoptimizer_t
+    deoptimizer_t,
+    training_replay_t
   };
 
+  static Handle create_thread_oop(const char* name, TRAPS);
   static JavaThread* make_thread(ThreadType type, jobject thread_oop, CompileQueue* queue, AbstractCompiler* comp, JavaThread* THREAD);
   static void init_compiler_threads();
+  static void init_training_replay();
   static void possibly_add_compiler_threads(JavaThread* THREAD);
   static bool compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level, bool excluded);
 
   static CompileTask* create_compile_task(CompileQueue*       queue,
                                           int                 compile_id,
                                           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);
   static void wait_for_completion(CompileTask* task);
 #if INCLUDE_JVMCI
   static bool wait_for_jvmci_completion(JVMCICompiler* comp, CompileTask* task, JavaThread* thread);
 #endif
@@ -273,17 +297,22 @@
                                   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);
 
-  static CompileQueue* compile_queue(int comp_level);
+  static CompileQueue* compile_queue(int comp_level, bool is_scc);
   static bool init_compiler_runtime();
   static void shutdown_compiler_runtime(AbstractCompiler* comp, CompilerThread* thread);
 
+  static SCCEntry* find_scc_entry(const methodHandle& method, int osr_bci, int comp_level,
+                                  CompileTask::CompileReason compile_reason,
+                                  bool requires_online_compilation);
+
 public:
   enum {
     // The entry bci used for non-OSR compilations.
     standard_entry_bci = InvocationEntryBci
   };
@@ -292,24 +321,28 @@
     if (is_c2_compile(comp_level)) return _compilers[1]; // C2
     if (is_c1_compile(comp_level)) return _compilers[0]; // C1
     return nullptr;
   }
 
-  static bool compilation_is_complete(const methodHandle& method, int osr_bci, int comp_level);
+  static bool initialized() { return _initialized; }
+  static bool replay_initialized() { return _replay_initialized; }
+  static bool compilation_is_complete(Method* method, int osr_bci, int comp_level, bool online_only,
+                                      CompileTask::CompileReason compile_reason);
   static bool compilation_is_in_queue(const methodHandle& method);
   static void print_compile_queues(outputStream* st);
-  static int queue_size(int comp_level) {
-    CompileQueue *q = compile_queue(comp_level);
+  static int queue_size(int comp_level, bool is_scc = false) {
+    CompileQueue *q = compile_queue(comp_level, is_scc);
     return q != nullptr ? q->size() : 0;
   }
   static void compilation_init(JavaThread* THREAD);
   static void init_compiler_thread_log();
   static nmethod* 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);
   static CompileQueue* c1_compile_queue();
   static CompileQueue* c2_compile_queue();
 
@@ -317,10 +350,11 @@
   static nmethod* 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);
 
 public:
@@ -401,12 +435,25 @@
     assert(_compiler2_objects != nullptr, "must be initialized");
     assert(idx < _c2_count, "oob");
     return _compiler2_objects[idx];
   }
 
+  static jobject compiler3_object(int idx) {
+    assert(_compiler3_objects != nullptr, "must be initialized");
+    assert(idx < _c3_count, "oob");
+    return _compiler3_objects[idx];
+  }
+
+  static jobject sc_object(int idx) {
+    assert(_sc_objects != nullptr, "must be initialized");
+    assert(idx < _sc_count, "oob");
+    return _sc_objects[idx];
+  }
+
   static AbstractCompiler* compiler1() { return _compilers[0]; }
   static AbstractCompiler* compiler2() { return _compilers[1]; }
+  static AbstractCompiler* compiler3() { return _compilers[2]; }
 
   static bool can_remove(CompilerThread *ct, bool do_it);
 
   static CompileLog* get_log(CompilerThread* ct);
 
@@ -425,14 +472,26 @@
   static int get_sum_nmethod_size() {               return _sum_nmethod_size;}
   static int get_sum_nmethod_code_size() {          return _sum_nmethod_code_size; }
   static jlong get_peak_compilation_time() {        return _peak_compilation_time; }
   static jlong get_total_compilation_time() {       return _t_total_compilation.milliseconds(); }
 
+  static void log_not_entrant(nmethod* nm);
+
   // Log that compilation profiling is skipped because metaspace is full.
   static void log_metaspace_failure();
 
+  static void print_statistics_on(outputStream* st);
+
   // CodeHeap State Analytics.
   static void print_info(outputStream *out);
   static void print_heapinfo(outputStream *out, const char* function, size_t granularity);
 };
 
+class TrainingReplayThread : public JavaThread {
+  static void training_replay_thread_entry(JavaThread* thread, TRAPS);
+public:
+  TrainingReplayThread() : JavaThread(&training_replay_thread_entry) { }
+
+  bool is_hidden_from_external_view() const      { return true; }
+};
+
 #endif // SHARE_COMPILER_COMPILEBROKER_HPP
diff a/src/hotspot/share/compiler/compileTask.cpp b/src/hotspot/share/compiler/compileTask.cpp
--- a/src/hotspot/share/compiler/compileTask.cpp
+++ b/src/hotspot/share/compiler/compileTask.cpp
@@ -33,10 +33,11 @@
 #include "memory/resourceArea.hpp"
 #include "oops/klass.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/jniHandles.hpp"
 #include "runtime/mutexLocker.hpp"
+#include "code/SCCache.hpp"
 
 CompileTask*  CompileTask::_task_free_list = nullptr;
 
 /**
  * Allocate a CompileTask, from the free list if possible.
@@ -90,41 +91,64 @@
                              const methodHandle& method,
                              int osr_bci,
                              int comp_level,
                              const methodHandle& hot_method,
                              int hot_count,
+                             SCCEntry* scc_entry,
                              CompileTask::CompileReason compile_reason,
+                             CompileQueue* compile_queue,
+                             bool requires_online_compilation,
                              bool is_blocking) {
   assert(!_lock->is_locked(), "bad locking");
 
   Thread* thread = Thread::current();
   _compile_id = compile_id;
   _method = method();
   _method_holder = JNIHandles::make_weak_global(Handle(thread, method->method_holder()->klass_holder()));
   _osr_bci = osr_bci;
+  _requires_online_compilation = requires_online_compilation;
   _is_blocking = is_blocking;
-  JVMCI_ONLY(_has_waiter = CompileBroker::compiler(comp_level)->is_jvmci();)
-  JVMCI_ONLY(_blocking_jvmci_compile_state = nullptr;)
   _comp_level = comp_level;
   _num_inlined_bytecodes = 0;
 
   _is_complete = false;
   _is_success = false;
 
+  _next = nullptr;
+  _prev = nullptr;
+
   _hot_method = nullptr;
   _hot_method_holder = nullptr;
   _hot_count = hot_count;
-  _time_queued = os::elapsed_counter();
+  _time_created = os::elapsed_counter();
+  _time_queued = 0;
   _time_started = 0;
+  _time_finished = 0;
   _compile_reason = compile_reason;
   _nm_content_size = 0;
-  AbstractCompiler* comp = compiler();
-  _directive = DirectivesStack::getMatchingDirective(method, comp);
   _nm_insts_size = 0;
   _nm_total_size = 0;
   _failure_reason = nullptr;
   _failure_reason_on_C_heap = false;
+  _training_data = nullptr;
+  _scc_entry = scc_entry;
+  _compile_queue = compile_queue;
+
+  AbstractCompiler* comp = CompileBroker::compiler(comp_level);
+#if INCLUDE_JVMCI
+  if (comp->is_jvmci() && CompileBroker::compiler3() != nullptr) {
+    assert(_method != nullptr, "sanity");
+    if (((JVMCICompiler*)comp)->force_comp_at_level_simple(method)) {
+      comp = CompileBroker::compiler3();
+    }
+  }
+#endif
+  _compiler = comp;
+  _directive = DirectivesStack::getMatchingDirective(method, comp);
+
+  JVMCI_ONLY(_has_waiter = comp->is_jvmci();)
+  JVMCI_ONLY(_blocking_jvmci_compile_state = nullptr;)
   _arena_bytes = 0;
 
   if (LogCompilation) {
     if (hot_method.not_null()) {
       if (hot_method == method) {
@@ -142,15 +166,19 @@
 
 /**
  * Returns the compiler for this task.
  */
 AbstractCompiler* CompileTask::compiler() const {
-  return CompileBroker::compiler(_comp_level);
+  assert(_compiler != nullptr, "should be set");
+  return _compiler;
 }
 
 // Replace weak handles by strong handles to avoid unloading during compilation.
 CompileTask* CompileTask::select_for_compilation() {
+  if (_compile_reason == Reason_Preload) {
+    return this;
+  }
   if (is_unloaded()) {
     // Guard against concurrent class unloading
     return nullptr;
   }
   Thread* thread = Thread::current();
@@ -175,10 +203,11 @@
     _hot_method->set_on_stack(true);
   }
 }
 
 bool CompileTask::is_unloaded() const {
+  if (preload()) return false;
   return _method_holder != nullptr && JNIHandles::is_weak_global_handle(_method_holder) && JNIHandles::is_weak_global_cleared(_method_holder);
 }
 
 // RedefineClasses support
 void CompileTask::metadata_do(MetadataClosure* f) {
@@ -201,11 +230,11 @@
 //
 // Otherwise it's the same as CompileTask::print_line()
 //
 void CompileTask::print_line_on_error(outputStream* st, char* buf, int buflen) {
   // print compiler name
-  st->print("%s:", CompileBroker::compiler_name(comp_level()));
+  st->print("%s:", compiler()->name());
   print(st);
 }
 
 // ------------------------------------------------------------------
 // CompileTask::print_tty
@@ -215,28 +244,47 @@
 }
 
 // ------------------------------------------------------------------
 // CompileTask::print_impl
 void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, int comp_level,
-                             bool is_osr_method, int osr_bci, bool is_blocking,
+                             bool is_osr_method, int osr_bci, bool is_blocking, bool is_scc, bool is_preload,
+                             const char* compiler_name,
                              const char* msg, bool short_form, bool cr,
-                             jlong time_queued, jlong time_started) {
+                             jlong time_created, jlong time_queued, jlong time_started, jlong time_finished) {
   if (!short_form) {
-    // Print current time
-    st->print(UINT64_FORMAT " ", (uint64_t) tty->time_stamp().milliseconds());
-    if (Verbose && time_queued != 0) {
-      // Print time in queue and time being processed by compiler thread
-      jlong now = os::elapsed_counter();
-      st->print("%.0f ", TimeHelper::counter_to_millis(now-time_queued));
-      if (time_started != 0) {
-        st->print("%.0f ", TimeHelper::counter_to_millis(now-time_started));
+    {
+      stringStream ss;
+      ss.print(UINT64_FORMAT, (uint64_t) tty->time_stamp().milliseconds());
+      st->print("%7s ", ss.freeze());
+    }
+    { // Time waiting to be put on queue
+      stringStream ss;
+      if (time_created != 0 && time_queued != 0) {
+        ss.print("W%.1f", TimeHelper::counter_to_millis(time_queued - time_created));
+      }
+      st->print("%7s ", ss.freeze());
+    }
+    { // Time in queue
+      stringStream ss;
+      if (time_queued != 0 && time_started != 0) {
+        ss.print("Q%.1f", TimeHelper::counter_to_millis(time_started - time_queued));
+      }
+      st->print("%7s ", ss.freeze());
+    }
+    { // Time in compilation
+      stringStream ss;
+      if (time_started != 0 && time_finished != 0) {
+        ss.print("C%.1f", TimeHelper::counter_to_millis(time_finished - time_started));
       }
+      st->print("%7s ", ss.freeze());
     }
+    st->print("  ");
   }
+
   // print compiler name if requested
   if (CIPrintCompilerName) {
-    st->print("%s:", CompileBroker::compiler_name(comp_level));
+    st->print("%s:", compiler_name);
   }
   st->print("%4d ", compile_id);    // print compilation number
 
   bool is_synchronized = false;
   bool has_exception_handler = false;
@@ -250,13 +298,15 @@
   const char compile_type   = is_osr_method                   ? '%' : ' ';
   const char sync_char      = is_synchronized                 ? 's' : ' ';
   const char exception_char = has_exception_handler           ? '!' : ' ';
   const char blocking_char  = is_blocking                     ? 'b' : ' ';
   const char native_char    = is_native                       ? 'n' : ' ';
+  const char scc_char       = is_scc                          ? 'A' : ' ';
+  const char preload_char   = is_preload                      ? 'P' : ' ';
 
   // print method attributes
-  st->print("%c%c%c%c%c ", compile_type, sync_char, exception_char, blocking_char, native_char);
+  st->print("%c%c%c%c%c%c%c ", compile_type, sync_char, exception_char, blocking_char, native_char, scc_char, preload_char);
 
   if (TieredCompilation) {
     if (comp_level != -1)  st->print("%d ", comp_level);
     else                   st->print("- ");
   }
@@ -300,11 +350,12 @@
 
 // ------------------------------------------------------------------
 // CompileTask::print_compilation
 void CompileTask::print(outputStream* st, const char* msg, bool short_form, bool cr) {
   bool is_osr_method = osr_bci() != InvocationEntryBci;
-  print_impl(st, is_unloaded() ? nullptr : method(), compile_id(), comp_level(), is_osr_method, osr_bci(), is_blocking(), msg, short_form, cr, _time_queued, _time_started);
+  print_impl(st, is_unloaded() ? nullptr : method(), compile_id(), comp_level(), is_osr_method, osr_bci(), is_blocking(), is_scc(), preload(),
+             compiler()->name(), msg, short_form, cr, _time_created, _time_queued, _time_started, _time_finished);
 }
 
 // ------------------------------------------------------------------
 // CompileTask::log_task
 void CompileTask::log_task(xmlStream* log) {
@@ -325,11 +376,10 @@
     log->print(" level='%d'", _comp_level);
   }
   if (_is_blocking) {
     log->print(" blocking='1'");
   }
-  log->stamp();
 }
 
 // ------------------------------------------------------------------
 // CompileTask::log_task_queued
 void CompileTask::log_task_queued() {
@@ -341,24 +391,26 @@
   log_task(xtty);
   assert(_compile_reason > CompileTask::Reason_None && _compile_reason < CompileTask::Reason_Count, "Valid values");
   xtty->print(" comment='%s'", reason_name(_compile_reason));
 
   if (_hot_method != nullptr && _hot_method != _method) {
-    xtty->method(_hot_method);
+    xtty->method(_hot_method, "hot_");
   }
   if (_hot_count != 0) {
     xtty->print(" hot_count='%d'", _hot_count);
   }
+  xtty->stamp();
   xtty->end_elem();
 }
 
 
 // ------------------------------------------------------------------
 // CompileTask::log_task_start
-void CompileTask::log_task_start(CompileLog* log)   {
+void CompileTask::log_task_start(CompileLog* log) {
   log->begin_head("task");
   log_task(log);
+  log->stamp();
   log->end_head();
 }
 
 
 // ------------------------------------------------------------------
@@ -465,11 +517,12 @@
   if (lt.is_enabled()) {
     LogStream ls(lt);
     print_impl(&ls, nm->method(), nm->compile_id(),
                nm->comp_level(), nm->is_osr_method(),
                nm->is_osr_method() ? nm->osr_entry_bci() : -1,
-               /*is_blocking*/ false,
+               /*is_blocking*/ false, nm->scc_entry() != nullptr,
+               nm->preloaded(), nm->compiler_name(),
                msg, /* short form */ true, /* cr */ true);
   }
 }
 
 void CompileTask::print_inlining_ul(ciMethod* method, int inline_level, int bci, InliningResult result, const char* msg) {
diff a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp
--- a/src/hotspot/share/compiler/compileTask.hpp
+++ b/src/hotspot/share/compiler/compileTask.hpp
@@ -29,11 +29,14 @@
 #include "code/nmethod.hpp"
 #include "compiler/compileLog.hpp"
 #include "memory/allocation.hpp"
 #include "utilities/xmlstream.hpp"
 
+class CompileQueue;
+class CompileTrainingData;
 class DirectiveSet;
+class SCCEntry;
 
 JVMCI_ONLY(class JVMCICompileState;)
 
 enum class InliningResult { SUCCESS, FAILURE };
 
@@ -60,10 +63,13 @@
       Reason_Tiered,           // Tiered-policy
       Reason_Replay,           // ciReplay
       Reason_Whitebox,         // Whitebox API
       Reason_MustBeCompiled,   // Used for -Xcomp or AlwaysCompileLoopMethods (see CompilationPolicy::must_be_compiled())
       Reason_Bootstrap,        // JVMCI bootstrap
+      Reason_Preload,          // pre-load SC code
+      Reason_Precompile,
+      Reason_PrecompileForPreload,
       Reason_Count
   };
 
   static const char* reason_name(CompileTask::CompileReason compile_reason) {
     static const char* reason_names[] = {
@@ -72,77 +78,101 @@
       "backedge_count",
       "tiered",
       "replay",
       "whitebox",
       "must_be_compiled",
-      "bootstrap"
+      "bootstrap",
+      "preload",
+      "precompile",
+      "precompile_for_preload",
     };
     return reason_names[compile_reason];
   }
 
+  static bool reason_is_precompiled(CompileTask::CompileReason compile_reason) {
+    return (compile_reason == CompileTask::Reason_Precompile) ||
+           (compile_reason == CompileTask::Reason_PrecompileForPreload);
+  }
+
  private:
   static CompileTask*  _task_free_list;
   Monitor*             _lock;
   int                  _compile_id;
   Method*              _method;
   jobject              _method_holder;
   int                  _osr_bci;
   bool                 _is_complete;
   bool                 _is_success;
+  bool                 _requires_online_compilation;
   bool                 _is_blocking;
   CodeSection::csize_t _nm_content_size;
   CodeSection::csize_t _nm_total_size;
   CodeSection::csize_t _nm_insts_size;
   DirectiveSet*  _directive;
+  AbstractCompiler*    _compiler;
+  SCCEntry*            _scc_entry;
 #if INCLUDE_JVMCI
   bool                 _has_waiter;
   // Compilation state for a blocking JVMCI compilation
   JVMCICompileState*   _blocking_jvmci_compile_state;
 #endif
   int                  _comp_level;
   int                  _num_inlined_bytecodes;
-  CompileTask*         _next, *_prev;
+  CompileTask*         _next;
+  CompileTask*         _prev;
   bool                 _is_free;
   // Fields used for logging why the compilation was initiated:
+  jlong                _time_created; // time when task was created
   jlong                _time_queued;  // time when task was enqueued
   jlong                _time_started; // time when compilation started
+  jlong                _time_finished; // time when compilation finished
   Method*              _hot_method;   // which method actually triggered this task
   jobject              _hot_method_holder;
   int                  _hot_count;    // information about its invocation counter
   CompileReason        _compile_reason;      // more info about the task
   const char*          _failure_reason;
   // Specifies if _failure_reason is on the C heap.
   bool                 _failure_reason_on_C_heap;
+  CompileTrainingData* _training_data;
+  CompileQueue*        _compile_queue;
   size_t               _arena_bytes;  // peak size of temporary memory during compilation (e.g. node arenas)
 
  public:
   CompileTask() : _failure_reason(nullptr), _failure_reason_on_C_heap(false) {
     // May hold MethodCompileQueue_lock
     _lock = new Monitor(Mutex::safepoint-1, "CompileTask_lock");
   }
 
   void initialize(int compile_id, const methodHandle& method, int osr_bci, int comp_level,
-                  const methodHandle& hot_method, int hot_count,
-                  CompileTask::CompileReason compile_reason, bool is_blocking);
+                  const methodHandle& hot_method, int hot_count, SCCEntry* scc_entry,
+                  CompileTask::CompileReason compile_reason,
+                  CompileQueue* compile_queue,
+                  bool requires_online_compilation, bool is_blocking);
 
   static CompileTask* allocate();
   static void         free(CompileTask* task);
 
-  int          compile_id() const                { return _compile_id; }
-  Method*      method() const                    { return _method; }
-  Method*      hot_method() const                { return _hot_method; }
-  int          osr_bci() const                   { return _osr_bci; }
-  bool         is_complete() const               { return _is_complete; }
-  bool         is_blocking() const               { return _is_blocking; }
-  bool         is_success() const                { return _is_success; }
-  DirectiveSet* directive() const                { return _directive; }
+  int          compile_id() const                   { return _compile_id; }
+  Method*      method() const                       { return _method; }
+  Method*      hot_method() const                   { return _hot_method; }
+  int          osr_bci() const                      { return _osr_bci; }
+  bool         is_complete() const                  { return _is_complete; }
+  bool         is_blocking() const                  { return _is_blocking; }
+  bool         is_success() const                   { return _is_success; }
+  bool         is_scc() const                       { return _scc_entry != nullptr; }
+  void         clear_scc()                          { _scc_entry = nullptr; }
+  SCCEntry*    scc_entry()                          { return _scc_entry; }
+  bool         requires_online_compilation() const  { return _requires_online_compilation; }
+  DirectiveSet* directive() const                   { return _directive; }
+  CompileReason compile_reason() const              { return _compile_reason; }
   CodeSection::csize_t nm_content_size() { return _nm_content_size; }
   void         set_nm_content_size(CodeSection::csize_t size) { _nm_content_size = size; }
   CodeSection::csize_t nm_insts_size() { return _nm_insts_size; }
   void         set_nm_insts_size(CodeSection::csize_t size) { _nm_insts_size = size; }
   CodeSection::csize_t nm_total_size() { return _nm_total_size; }
   void         set_nm_total_size(CodeSection::csize_t size) { _nm_total_size = size; }
+  bool         preload() const                   { return (_compile_reason == Reason_Preload); }
   bool         can_become_stale() const          {
     switch (_compile_reason) {
       case Reason_BackedgeCount:
       case Reason_InvocationCount:
       case Reason_Tiered:
@@ -170,53 +200,71 @@
   void         set_blocking_jvmci_compile_state(JVMCICompileState* state) {
     _blocking_jvmci_compile_state = state;
   }
 #endif
 
+  bool is_precompiled() {
+    return reason_is_precompiled(compile_reason());
+  }
+
   Monitor*     lock() const                      { return _lock; }
 
+  CompileQueue* compile_queue() const            { return _compile_queue; }
+
   void         mark_complete()                   { _is_complete = true; }
   void         mark_success()                    { _is_success = true; }
+  void         mark_queued(jlong time)           { _time_queued = time; }
   void         mark_started(jlong time)          { _time_started = time; }
+  void         mark_finished(jlong time)         { _time_finished = time; }
 
   int          comp_level()                      { return _comp_level;}
   void         set_comp_level(int comp_level)    { _comp_level = comp_level;}
 
   AbstractCompiler* compiler() const;
   CompileTask*      select_for_compilation();
 
   int          num_inlined_bytecodes() const     { return _num_inlined_bytecodes; }
   void         set_num_inlined_bytecodes(int n)  { _num_inlined_bytecodes = n; }
 
+  static CompileTask* volatile* next_ptr(CompileTask& task) { return &task._next; }
+
   CompileTask* next() const                      { return _next; }
   void         set_next(CompileTask* next)       { _next = next; }
   CompileTask* prev() const                      { return _prev; }
   void         set_prev(CompileTask* prev)       { _prev = prev; }
   bool         is_free() const                   { return _is_free; }
   void         set_is_free(bool val)             { _is_free = val; }
   bool         is_unloaded() const;
 
+  CompileTrainingData* training_data() const     { return _training_data; }
+  void set_training_data(CompileTrainingData*td) { _training_data = td; }
+
+  CompileReason compile_reason()                 { return _compile_reason; }
+
   // RedefineClasses support
   void         metadata_do(MetadataClosure* f);
   void         mark_on_stack();
 
   void         set_arena_bytes(size_t s)         { _arena_bytes = s; }
   size_t       arena_bytes() const               { return _arena_bytes; }
 
 private:
   static void  print_impl(outputStream* st, Method* method, int compile_id, int comp_level,
                                       bool is_osr_method = false, int osr_bci = -1, bool is_blocking = false,
+                                      bool is_scc = false, bool is_preload = false,
+                                      const char* compiler_name = nullptr,
                                       const char* msg = nullptr, bool short_form = false, bool cr = true,
-                                      jlong time_queued = 0, jlong time_started = 0);
+                                      jlong time_created = 0, jlong time_queued = 0, jlong time_started = 0, jlong time_finished = 0);
 
 public:
   void         print(outputStream* st = tty, const char* msg = nullptr, bool short_form = false, bool cr = true);
   void         print_ul(const char* msg = nullptr);
   static void  print(outputStream* st, const nmethod* nm, const char* msg = nullptr, bool short_form = false, bool cr = true) {
     print_impl(st, nm->method(), nm->compile_id(), nm->comp_level(),
                            nm->is_osr_method(), nm->is_osr_method() ? nm->osr_entry_bci() : -1, /*is_blocking*/ false,
-                           msg, short_form, cr);
+                           nm->scc_entry() != nullptr, nm->preloaded(),
+                           nm->compiler_name(), msg, short_form, cr);
   }
   static void  print_ul(const nmethod* nm, const char* msg = nullptr);
 
   static void  print_inline_indent(int inline_level, outputStream* st = tty);
 
diff a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp
--- a/src/hotspot/share/compiler/compilerDefinitions.hpp
+++ b/src/hotspot/share/compiler/compilerDefinitions.hpp
@@ -57,11 +57,12 @@
   CompLevel_all               = -1,        // Used for changing the state
   CompLevel_none              = 0,         // Interpreter
   CompLevel_simple            = 1,         // C1
   CompLevel_limited_profile   = 2,         // C1, invocation & backedge counters
   CompLevel_full_profile      = 3,         // C1, invocation & backedge counters + mdo
-  CompLevel_full_optimization = 4          // C2 or JVMCI
+  CompLevel_full_optimization = 4,         // C2 or JVMCI
+  CompLevel_count             = 4
 };
 
 class CompilationModeFlag : AllStatic {
   enum class Mode {
     NORMAL,
diff a/src/hotspot/share/compiler/compilerDirectives.hpp b/src/hotspot/share/compiler/compilerDirectives.hpp
--- a/src/hotspot/share/compiler/compilerDirectives.hpp
+++ b/src/hotspot/share/compiler/compilerDirectives.hpp
@@ -51,11 +51,17 @@
     cflags(BackgroundCompilation,   bool, BackgroundCompilation, BackgroundCompilation) \
     cflags(ReplayInline,            bool, false, ReplayInline) \
     cflags(DumpReplay,              bool, false, DumpReplay) \
     cflags(DumpInline,              bool, false, DumpInline) \
     cflags(CompilerDirectivesIgnoreCompileCommands, bool, CompilerDirectivesIgnoreCompileCommands, Unknown) \
-    cflags(RepeatCompilation,       intx, RepeatCompilation, RepeatCompilation)
+    cflags(RepeatCompilation,       intx, RepeatCompilation, RepeatCompilation) \
+    cflags(PrecompileRecorded,      uintx, 0, PrecompileRecorded) \
+    cflags(DontPrecompile,          bool, false, DontPrecompile) \
+    cflags(DontPreload,             bool, false, DontPreload) \
+    cflags(IgnoreRecordedProfile,   bool, false, IgnoreRecordedProfile) \
+    cflags(IgnorePrecompiled,       bool, false, IgnorePrecompiled) \
+    cflags(TooManyTrapsAtBCI,       ccstrlist, "", TooManyTrapsAtBCI)
 #define compilerdirectives_common_string_flags(cflags)                           \
   cflags(DisableIntrinsic,        ccstrlist, DisableIntrinsic, DisableIntrinsic) \
   cflags(ControlIntrinsic,        ccstrlist, ControlIntrinsic, ControlIntrinsic)
 #define compilerdirectives_common_flags(cflags) \
   compilerdirectives_common_other_flags(cflags) \
diff a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp
--- a/src/hotspot/share/compiler/compilerOracle.cpp
+++ b/src/hotspot/share/compiler/compilerOracle.cpp
@@ -818,10 +818,12 @@
         if (!validator.is_valid()) {
           jio_snprintf(errorbuf, buf_size, "Unrecognized phase name in %s: %s", option2name(option), validator.what());
         }
       } else if (option == CompileCommandEnum::TestOptionList) {
         // all values are ok
+      } else if (option == CompileCommandEnum::TooManyTrapsAtBCI) {
+        // FIXME
       }
 #endif
       else {
         assert(false, "Ccstrlist type option missing validator");
       }
diff a/src/hotspot/share/compiler/compilerOracle.hpp b/src/hotspot/share/compiler/compilerOracle.hpp
--- a/src/hotspot/share/compiler/compilerOracle.hpp
+++ b/src/hotspot/share/compiler/compilerOracle.hpp
@@ -89,10 +89,16 @@
 NOT_PRODUCT(option(TraceMergeStores, "TraceMergeStores", Ccstrlist)) \
   option(Vectorize, "Vectorize", Bool) \
   option(CloneMapDebug, "CloneMapDebug", Bool) \
   option(IncrementalInlineForceCleanup, "IncrementalInlineForceCleanup", Bool) \
   option(MaxNodeLimit, "MaxNodeLimit", Intx)  \
+  option(PrecompileRecorded, "PrecompileRecorded", Uintx) \
+  option(DontPrecompile, "DontPrecompile", Bool) \
+  option(DontPreload, "DontPreload", Bool) \
+  option(IgnorePrecompiled, "IgnorePrecompiled", Bool) \
+  option(IgnoreRecordedProfile, "IgnoreRecordedProfile", Bool) \
+  option(TooManyTrapsAtBCI, "TooManyTrapsAtBCI", Ccstrlist) \
 NOT_PRODUCT(option(TestOptionInt,    "TestOptionInt",    Intx)) \
 NOT_PRODUCT(option(TestOptionUint,   "TestOptionUint",   Uintx)) \
 NOT_PRODUCT(option(TestOptionBool,   "TestOptionBool",   Bool)) \
 NOT_PRODUCT(option(TestOptionBool2,  "TestOptionBool2",  Bool)) \
 NOT_PRODUCT(option(TestOptionStr,    "TestOptionStr",    Ccstr)) \
diff a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp
--- a/src/hotspot/share/compiler/compiler_globals.hpp
+++ b/src/hotspot/share/compiler/compiler_globals.hpp
@@ -267,10 +267,21 @@
                                                                             \
   product(intx, TieredRateUpdateMaxTime, 25,                                \
           "Maximum rate sampling interval (in milliseconds)")               \
           range(0, max_intx)                                                \
                                                                             \
+  product(double, Tier0ProfileDelayFactor, 100.0, DIAGNOSTIC,               \
+          "Delay profiling/compiling of methods that were "                 \
+          "observed to be lukewarm")                                        \
+                                                                            \
+  product(double, Tier2ProfileDelayFactor, 250.0, DIAGNOSTIC,               \
+          "Delay profiling of methods that were observed to be lukewarm")   \
+                                                                            \
+  product(bool, SkipTier2IfPossible, false, DIAGNOSTIC,                     \
+          "Compile at tier 4 instead of tier 2 in training replay "         \
+          "mode if posssible")                                              \
+                                                                            \
   product(ccstr, CompilationMode, "default",                                \
           "Compilation modes: "                                             \
           "default: normal tiered compilation; "                            \
           "quick-only: C1-only mode; "                                      \
           "high-only: C2/JVMCI-only mode; "                                 \
@@ -380,10 +391,107 @@
                                                                             \
   product(bool, CaptureBailoutInformation, trueInDebug, DIAGNOSTIC,         \
           "If compilation is stopped with an error, capture diagnostic "    \
           "information at the bailout point")                               \
                                                                             \
+  /* flags to control training and deployment modes  */                     \
+                                                                            \
+  product(bool, RecordTraining, false,                                      \
+          "Request output of training data for improved deployment.")       \
+                                                                            \
+  product(bool, ReplayTraining, false,                                      \
+          "Read training data, if available, for use in this execution")    \
+                                                                            \
+  product(bool, PrintTrainingInfo, false, DIAGNOSTIC,                       \
+          "Print additional information about training")                    \
+                                                                            \
+  product(bool, RecordOptCompilationOrder, false,                           \
+          "Record c2/jvmci nmethod temperature to guide compilation order.")\
+                                                                            \
+  product(bool, RecordOnlyTopCompilations, false,                           \
+          "Record only top compilations (non-zero counts)")                 \
+                                                                            \
+  product(int, RecordOptCompilationOrderInterval, 10,                       \
+          "Sampling interval for RecordOptCompilationOrder")                \
+                                                                            \
+  /* Code Caching flags */                                                  \
+                                                                            \
+  product(bool, UseC2asC3, false,                                           \
+          "Use C2 as 3rd compiler when other high-optimizing compiler "     \
+          "is used")                                                        \
+                                                                            \
+  product(bool, StoreCachedCode, false,                                     \
+          "Store cached compiled code")                                     \
+                                                                            \
+  product(bool, LoadCachedCode, false,                                      \
+          "Load cached compiled code")                                      \
+                                                                            \
+  product(uint, DisableCachedCode, 0,                                       \
+          "Disable cached code on some compilation levels "                 \
+          "(T1=1; T2=2; T4=4; T5/preload=8")                                \
+                                                                            \
+  product(uint, ClassInitBarrierMode, 0,                                    \
+          "Produce and use startup code which could be called "             \
+          "on first method invocation, add class initialization barriers, " \
+          "other checks and constrains if needed "                          \
+          "(0: no barriers; 1: uncommon trap; 2: full barrier)")            \
+                                                                            \
+  product(bool, StressClassInitBarriers, false, DIAGNOSTIC,                 \
+          "Force slow path in class initialization barriers")               \
+                                                                            \
+  product(bool, UseMetadataPointers, true,                                  \
+          "Store Metadata pointers in Relocation Info for cached code")     \
+                                                                            \
+  product(bool, UseCodeLoadThread, true,                                    \
+          "Use separate thread for cached code load")                       \
+                                                                            \
+  product(uint, SCLoadStart, 0,                                             \
+          "The id of the first cached code to load")                        \
+                                                                            \
+  product(uint, SCLoadStop, max_jint,                                       \
+          "The id of the last cached code to load")                         \
+                                                                            \
+  product(ccstr, CachedCodeFile, "code.jsa",                                \
+          "File with cached compiled code")                                 \
+                                                                            \
+  product(uint, CachedCodeMaxSize, 10*M,                                    \
+          "Buffer size in bytes for code caching")                          \
+                                                                            \
+  product(bool, VerifyCachedCode, false, DIAGNOSTIC,                        \
+          "Load compiled code but not publish")                             \
+                                                                            \
+  /* Recompilation flags */                                                 \
+                                                                            \
+  product(int, RecompilationLoadAverageThreshold, 5,                        \
+          "Queues load avergage after while recompilations are allowed")    \
+                                                                            \
+  product(int, RecompilationWorkUnitSize, 5,                                \
+          "Queues load avergage after while recompilations are allowed")    \
+                                                                            \
+  product(bool, UseRecompilation, false,                                    \
+          "Recompile methods for peak performance")                         \
+                                                                            \
+  product(bool, ForceRecompilation, false,                                  \
+          "Testing mode for recompilation")                                 \
+                                                                            \
+  product(double, DelayRecompilation, 0.0,                                  \
+          "Delay recompilation for given number of seconds")                \
+                                                                            \
+  product(bool, UseConcurrentTrainingReplay, true,                          \
+          "Replay training after class initialization in a separate thread")\
+                                                                            \
+  product(bool, UseGlobalCompileQueueLock, false,                           \
+          "Use a global lock for all compilation queues")                   \
+                                                                            \
+  product(bool, UseLockFreeCompileQueues, true,                             \
+          "Use lock free compile queues")                                   \
+                                                                            \
+  product(bool, PrecompileCode, false,                                      \
+          "Precompile code")                                                \
+                                                                            \
+  product(bool, PrecompileOnlyAndExit, false,                               \
+          "Exit after precompilation step is over")                         \
 
 // end of COMPILER_FLAGS
 
 DECLARE_FLAGS(COMPILER_FLAGS)
 
diff a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp
--- a/src/hotspot/share/compiler/disassembler.cpp
+++ b/src/hotspot/share/compiler/disassembler.cpp
@@ -605,13 +605,11 @@
       }
       st->print("Stub::<unknown> " PTR_FORMAT, p2i(adr));
       return;
     }
 
-    BarrierSet* bs = BarrierSet::barrier_set();
-    if (bs->is_a(BarrierSet::CardTableBarrierSet) &&
-        adr == ci_card_table_address_as<address>()) {
+    if (is_card_table_address(adr)) {
       st->print("word_map_base");
       if (WizardMode) st->print(" " INTPTR_FORMAT, p2i(adr));
       return;
     }
   }
diff a/src/hotspot/share/compiler/methodMatcher.cpp b/src/hotspot/share/compiler/methodMatcher.cpp
--- a/src/hotspot/share/compiler/methodMatcher.cpp
+++ b/src/hotspot/share/compiler/methodMatcher.cpp
@@ -283,18 +283,19 @@
 
     // Over-consumption
     // method_name points to an option type or option name because the method name is not specified by users.
     // In very rare case, the method name happens to be same as option type/name, so look ahead to make sure
     // it doesn't show up again.
-    if ((OptionType::Unknown != CompilerOracle::parse_option_type(method_name) ||
-        CompileCommandEnum::Unknown != CompilerOracle::parse_option_name(method_name)) &&
-        *(line + bytes_read) != '\0' &&
-        strstr(line + bytes_read, method_name) == nullptr) {
-      error_msg = "Did not specify any method name";
-      method_name[0] = '\0';
-      return;
-    }
+    // !!! FIXME !!! rejects TooManyTrapsAtBCI,CLS::print()V,199 command
+//    if ((OptionType::Unknown != CompilerOracle::parse_option_type(method_name) ||
+//        CompileCommandEnum::Unknown != CompilerOracle::parse_option_name(method_name)) &&
+//        *(line + bytes_read) != '\0' &&
+//        strstr(line + bytes_read, method_name) == nullptr) {
+//      error_msg = "Did not specify any method name";
+//      method_name[0] = '\0';
+//      return;
+//    }
 
     if ((strchr(class_name, JVM_SIGNATURE_SPECIAL) != nullptr) ||
         (strchr(class_name, JVM_SIGNATURE_ENDSPECIAL) != nullptr)) {
       error_msg = "Chars '<' and '>' not allowed in class name";
       return;
@@ -357,10 +358,19 @@
     return true;
   }
   return false;
 }
 
+bool MethodMatcher::matches(MethodDetails& method_details) const {
+  if (match(method_details.class_name(), this->class_name(), _class_mode) &&
+      match(method_details.method_name(), this->method_name(), _method_mode) &&
+      ((this->signature() == nullptr) || match(method_details.signature(), this->signature(), Prefix))) {
+    return true;
+  }
+  return false;
+}
+
 void MethodMatcher::print_symbol(outputStream* st, Symbol* h, Mode mode) {
   if (mode == Suffix || mode == Substring || mode == Any) {
     st->print("*");
   }
   if (mode != Any) {
@@ -401,10 +411,19 @@
     }
   }
   return bm;
 }
 
+bool BasicMatcher::match(MethodDetails& method_details) {
+  for (BasicMatcher* current = this; current != nullptr; current = current->next()) {
+    if (current->matches(method_details)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool BasicMatcher::match(const methodHandle& method) {
   for (BasicMatcher* current = this; current != nullptr; current = current->next()) {
     if (current->matches(method)) {
       return true;
     }
diff a/src/hotspot/share/compiler/methodMatcher.hpp b/src/hotspot/share/compiler/methodMatcher.hpp
--- a/src/hotspot/share/compiler/methodMatcher.hpp
+++ b/src/hotspot/share/compiler/methodMatcher.hpp
@@ -25,10 +25,11 @@
 #ifndef SHARE_COMPILER_METHODMATCHER_HPP
 #define SHARE_COMPILER_METHODMATCHER_HPP
 
 #include "memory/allocation.hpp"
 #include "runtime/handles.hpp"
+#include "runtime/methodDetails.hpp"
 #include "memory/resourceArea.hpp"
 
 class MethodMatcher : public CHeapObj<mtCompiler> {
  public:
   enum Mode {
@@ -59,10 +60,11 @@
 
   void init(Symbol* class_name, Mode class_mode, Symbol* method_name, Mode method_mode, Symbol* signature);
   static void parse_method_pattern(char*& line, const char*& error_msg, MethodMatcher* m);
   static void print_symbol(outputStream* st, Symbol* h, Mode mode);
   bool matches(const methodHandle& method) const;
+  bool matches(MethodDetails& method_details) const;
   void print_base(outputStream* st);
 
  private:
   static bool canonicalize(char * line, const char *& error_msg);
   bool match(Symbol* candidate, Symbol* match, Mode match_mode) const;
@@ -81,10 +83,11 @@
     _next(next) {
   }
 
   static BasicMatcher* parse_method_pattern(char* line, const char*& error_msg, bool expect_trailing_chars);
   bool match(const methodHandle& method);
+  bool match(MethodDetails& method_details);
   void set_next(BasicMatcher* next) { _next = next; }
   BasicMatcher* next() { return _next; }
 
   void print(outputStream* st) { print_base(st); }
   void print_all(outputStream* st) {
diff a/src/hotspot/share/compiler/oopMap.cpp b/src/hotspot/share/compiler/oopMap.cpp
--- a/src/hotspot/share/compiler/oopMap.cpp
+++ b/src/hotspot/share/compiler/oopMap.cpp
@@ -113,10 +113,22 @@
   _locs_used   = NEW_RESOURCE_ARRAY(OopMapValue::oop_types, _locs_length);
   for(int i = 0; i < _locs_length; i++) _locs_used[i] = OopMapValue::unused_value;
 #endif
 }
 
+OopMap::OopMap(int data_size) {
+  // OopMaps are usually quite so small, so pick a small initial size
+  set_write_stream(new CompressedWriteStream(data_size));
+  set_omv_count(0);
+  _num_oops = 0;
+  _has_derived_oops = false;
+  _index = -1;
+#ifdef ASSERT
+  _locs_length = 0;
+  _locs_used   = nullptr;
+#endif
+}
 
 OopMap::OopMap(OopMap::DeepCopyToken, OopMap* source) {
   // This constructor does a deep copy
   // of the source OopMap.
   set_write_stream(new CompressedWriteStream(source->omv_count() * 2));
@@ -366,10 +378,12 @@
 
 // OopMapSet
 
 OopMapSet::OopMapSet() : _list(MinOopMapAllocation) {}
 
+OopMapSet::OopMapSet(int size) : _list(size) {}
+
 int OopMapSet::add_gc_map(int pc_offset, OopMap *map ) {
   map->set_offset(pc_offset);
 
 #ifdef ASSERT
   if(_list.length() > 0) {
diff a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp
--- a/src/hotspot/share/compiler/oopMap.hpp
+++ b/src/hotspot/share/compiler/oopMap.hpp
@@ -152,10 +152,11 @@
 class OopMap: public ResourceObj {
   friend class OopMapStream;
   friend class VMStructs;
   friend class OopMapSet;
   friend class OopMapSort;
+  friend class SCCReader;
  private:
   int  _pc_offset; // offset in the code that this OopMap corresponds to
   int  _omv_count; // number of OopMapValues in the stream
   int  _num_oops;  // number of oops
   int  _index;     // index in OopMapSet
@@ -178,10 +179,11 @@
 
   void set_xxx(VMReg reg, OopMapValue::oop_types x, VMReg optional);
 
  public:
   OopMap(int frame_size, int arg_count);
+  OopMap(int data_size);
 
   // pc-offset handling
   int offset() const     { return _pc_offset; }
   void set_offset(int o) { _pc_offset = o; }
   int count() const { return _omv_count; }
@@ -214,17 +216,19 @@
   bool equals(const OopMap* other) const;
 };
 
 class OopMapSet : public ResourceObj {
   friend class VMStructs;
+  friend class SCCReader;
  private:
   GrowableArray<OopMap*> _list;
 
   int add(OopMap* value) { return _list.append(value); }
 
  public:
   OopMapSet();
+  OopMapSet(int size);
 
   // returns the number of OopMaps in this OopMapSet
   int size() const            { return _list.length(); }
   // returns the OopMap at a given index
   OopMap* at(int index) const { return _list.at(index); }
diff a/src/hotspot/share/compiler/precompiler.cpp b/src/hotspot/share/compiler/precompiler.cpp
--- /dev/null
+++ b/src/hotspot/share/compiler/precompiler.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2023, 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "cds/archiveBuilder.hpp"
+#include "cds/cdsAccess.hpp"
+#include "cds/cdsConfig.hpp"
+#include "cds/runTimeClassInfo.hpp"
+#include "code/SCCache.hpp"
+#include "compiler/compiler_globals.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/precompiler.hpp"
+#include "logging/logStream.hpp"
+#include "memory/allocation.hpp"
+#include "oops/trainingData.hpp"
+#include "runtime/handles.inline.hpp"
+
+static int precompiled_total_count = 0;
+
+class PrecompileIterator : StackObj {
+private:
+  CompLevel _comp_level;
+  CompLevel _search_level;
+  bool _for_preload;
+  Thread* _thread;
+  GrowableArray<Method*> _methods;
+
+  bool precompile(Method* m, TRAPS) {
+    assert(!HAS_PENDING_EXCEPTION, "");
+    assert(m->method_holder()->is_linked(), "required");
+
+    ++precompiled_total_count;
+
+    uint entries_cnt_before = SCCache::store_entries_cnt();
+
+    methodHandle mh(THREAD, m);
+    CompileTask::CompileReason compile_reason = (_for_preload ? CompileTask::Reason_PrecompileForPreload
+                                                              : CompileTask::Reason_Precompile);
+    nmethod* nm = CompileBroker::compile_method(mh, InvocationEntryBci, _comp_level, methodHandle(), 0,
+                                                true /*requires_online_comp*/, compile_reason,
+                                                THREAD);
+
+    uint entries_cnt_after = SCCache::store_entries_cnt();
+
+    return (nm != nullptr) || (entries_cnt_after > entries_cnt_before);
+  }
+
+  bool precompile(Method* m, ArchiveBuilder* builder, TRAPS) {
+    bool status = precompile(m, THREAD);
+
+    LogStreamHandle(Info, precompile) log;
+    if (log.is_enabled()) {
+      ResourceMark rm;
+      log.print("[%4d] T%d Compiled %s [%p",
+                precompiled_total_count, _comp_level + (_for_preload ? 1 : 0), m->external_name(), m);
+      if (builder != nullptr) {
+        Method* requested_m = builder->to_requested(builder->get_buffered_addr(m));
+        log.print(" -> %p", requested_m);
+      }
+      log.print("] [%d] (%s)", SCCache::store_entries_cnt(), (status ? "success" : "FAILED"));
+    }
+    return status;
+  }
+
+public:
+  PrecompileIterator(CompLevel comp_level, bool for_preload, CompLevel search_level, JavaThread* thread)
+  : _comp_level(comp_level), _search_level(search_level), _for_preload(for_preload), _thread(thread) {
+    assert(TrainingData::have_data(), "sanity");
+  }
+
+  bool include(Method* m) {
+    if (m->is_native() || m->is_abstract()) {
+      return false;
+    }
+    DirectiveSet* directives = DirectivesStack::getMatchingDirective(methodHandle(_thread, m), nullptr);
+    if (directives->DontPrecompileOption) {
+      return false; // excluded
+    } else if (directives->PrecompileRecordedOption > 0) {
+      return true;
+    }
+    int cid = compile_id(m, _search_level);
+    return (cid < INT_MAX);
+  }
+
+  void do_value(const RunTimeClassInfo* record) {
+    Array<Method*>* methods = record->klass()->methods();
+    for (int i = 0; i < methods->length(); i++) {
+      Method* m = methods->at(i);
+      if (include(m)) {
+        _methods.push(m);
+      }
+    }
+  }
+  void do_value(TrainingData* td) {
+    if (td->is_MethodTrainingData()) {
+      MethodTrainingData* mtd = td->as_MethodTrainingData();
+      if (mtd->has_holder() && include((Method*)mtd->holder())) {
+        _methods.push((Method*)mtd->holder());
+      }
+    }
+  }
+
+  static int compile_id(Method* m, int level) {
+    MethodTrainingData* mtd = TrainingData::lookup_for(m);
+    if (mtd != nullptr && mtd->highest_level() == level) {
+      CompileTrainingData* ctd = mtd->last_toplevel_compile(level);
+      if (ctd != nullptr) {
+        return ctd->compile_id();
+      }
+    }
+    return INT_MAX; // treat as the last compilation
+  }
+
+  static int compare_by_compile_id(Method** m1, Method** m2, CompLevel comp_level) {
+    int id1 = compile_id(*m1, comp_level);
+    int id2 = compile_id(*m2, comp_level);
+    return (id1 - id2);
+  }
+
+  static int compare_by_compile_id_tier1(Method** m1, Method** m2) {
+    return compare_by_compile_id(m1, m2, CompLevel_simple);
+  }
+
+  static int compare_by_compile_id_tier2(Method** m1, Method** m2) {
+    return compare_by_compile_id(m1, m2, CompLevel_limited_profile);
+  }
+
+  static int compare_by_compile_id_tier3(Method** m1, Method** m2) {
+    return compare_by_compile_id(m1, m2, CompLevel_full_profile);
+  }
+
+  static int compare_by_compile_id_tier4(Method** m1, Method** m2) {
+    return compare_by_compile_id(m1, m2, CompLevel_full_optimization);
+  }
+
+  void sort_methods_by_compile_id(GrowableArray<Method*>* methods) {
+    switch(_search_level) {
+      case CompLevel_simple:            methods->sort(&compare_by_compile_id_tier1); break;
+      case CompLevel_limited_profile:   methods->sort(&compare_by_compile_id_tier2); break;
+      case CompLevel_full_profile:      methods->sort(&compare_by_compile_id_tier3); break;
+      case CompLevel_full_optimization: methods->sort(&compare_by_compile_id_tier4); break;
+
+      default: fatal("%d", _search_level);
+    }
+  }
+
+  void precompile(ArchiveBuilder* builder, TRAPS) {
+    sort_methods_by_compile_id(&_methods);
+
+    for (int i = 0; i < _methods.length(); i++) {
+      Method* m = _methods.at(i);
+
+      assert(!HAS_PENDING_EXCEPTION, "");
+      precompile(m, builder, THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        CLEAR_PENDING_EXCEPTION;
+      }
+    }
+  }
+};
+void Precompiler::compile_cached_code(CompLevel search_level, bool for_preload, CompLevel comp_level, TRAPS) {
+  ResourceMark rm;
+  PrecompileIterator pi(comp_level, for_preload, search_level, THREAD);
+  TrainingData::archived_training_data_dictionary()->iterate(&pi);
+  pi.precompile((ArchiveBuilder*)nullptr, THREAD);
+}
+
+void Precompiler::compile_cached_code(TRAPS) {
+  log_info(precompile)("Precompilation started");
+  if (TrainingData::have_data()) {
+    TrainingData::archived_training_data_dictionary()->iterate([&](TrainingData* td) {
+      if (td->is_KlassTrainingData()) {
+        KlassTrainingData *ktd = td->as_KlassTrainingData();
+        if (ktd->has_holder()) {
+          assert(!HAS_PENDING_EXCEPTION, "");
+          ktd->holder()->link_class(THREAD);
+          if (HAS_PENDING_EXCEPTION) {
+            LogStreamHandle(Warning, precompile) log;
+            if (log.is_enabled()) {
+              ResourceMark rm;
+              log.print("Linkage failed for %s: ", ktd->holder()->external_name());
+              PENDING_EXCEPTION->print_on(&log);
+            }
+            CLEAR_PENDING_EXCEPTION;
+          }
+        }
+      }
+    });
+
+    compile_cached_code(CompLevel_full_optimization, true, CompLevel_full_optimization, CHECK);
+
+    compile_cached_code(CompLevel_full_optimization, false, CompLevel_full_optimization, CHECK);
+    compile_cached_code(CompLevel_full_profile,      false, CompLevel_limited_profile,   CHECK);
+    compile_cached_code(CompLevel_limited_profile,   false, CompLevel_limited_profile,   CHECK);
+    compile_cached_code(CompLevel_simple,            false, CompLevel_simple,            CHECK);
+  }
+  log_info(precompile)("Precompilation finished (total: %d)", precompiled_total_count);
+}
+
+// New workflow only
+void Precompiler::compile_cached_code(ArchiveBuilder* builder, TRAPS) {
+  assert(CDSConfig::is_dumping_final_static_archive() && StoreCachedCode, "sanity");
+  if (TrainingData::have_data()) {
+    ResourceMark rm;
+
+    SCCache::new_workflow_start_writing_cache();
+
+    {
+      PrecompileIterator pi(CompLevel_full_optimization, true /*for_preload*/, CompLevel_full_optimization, THREAD);
+      TrainingData::archived_training_data_dictionary()->iterate(&pi);
+      pi.precompile(builder, THREAD);
+    }
+
+    for (int level = CompLevel_simple; level <= CompLevel_full_optimization; level++) {
+      CompLevel comp_level = (CompLevel)level;
+      if (comp_level == CompLevel_full_profile) {
+        comp_level = CompLevel_limited_profile;
+      }
+      PrecompileIterator pi(comp_level, false /*for_preload*/, (CompLevel)level, THREAD);
+      TrainingData::archived_training_data_dictionary()->iterate(&pi);
+      pi.precompile(builder, THREAD);
+    }
+
+    SCCache::new_workflow_end_writing_cache();
+  }
+}
diff a/src/hotspot/share/compiler/precompiler.hpp b/src/hotspot/share/compiler/precompiler.hpp
--- /dev/null
+++ b/src/hotspot/share/compiler/precompiler.hpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#ifndef SHARE_COMPILER_PRECOMPILER_HPP
+#define SHARE_COMPILER_PRECOMPILER_HPP
+
+#include "memory/allStatic.hpp"
+#include "utilities/exceptions.hpp"
+
+class ArchiveBuilder;
+
+class Precompiler : AllStatic  {
+private:
+  static int _total_count;
+
+public:
+  static void compile_cached_code(CompLevel search_level, bool for_preload, CompLevel comp_level, TRAPS);
+  static void compile_cached_code(TRAPS);
+  static void compile_cached_code(ArchiveBuilder* builder, TRAPS);
+};
+
+#endif // SHARE_COMPILER_PRECOMPILER_HPP
diff a/src/hotspot/share/compiler/recompilationPolicy.cpp b/src/hotspot/share/compiler/recompilationPolicy.cpp
--- /dev/null
+++ b/src/hotspot/share/compiler/recompilationPolicy.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "compiler/compilationPolicy.hpp"
+#include "compiler/recompilationPolicy.hpp"
+#include "oops/method.inline.hpp"
+#include "oops/recompilationSchedule.hpp"
+#include "runtime/handles.inline.hpp"
+
+RecompilationPolicy::LoadAverage RecompilationPolicy::_load_average;
+volatile bool RecompilationPolicy::_recompilation_done = false;
+
+void RecompilationPolicy::sample_load_average() {
+  if (UseRecompilation) {
+    const int c2_queue_size = CompileBroker::queue_size(CompLevel_full_optimization);
+    _load_average.sample(c2_queue_size);
+  }
+}
+
+void RecompilationPolicy::print_load_average() {
+  tty->print(" load=%lf", _load_average.value());
+}
+
+bool RecompilationPolicy::have_recompilation_work() {
+  if (UseRecompilation && TrainingData::have_data() && RecompilationSchedule::have_schedule() &&
+                          RecompilationSchedule::length() > 0 && !_recompilation_done) {
+    if (_load_average.value() <= RecompilationLoadAverageThreshold) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool RecompilationPolicy::recompilation_step(int step, TRAPS) {
+  if (!have_recompilation_work() || os::elapsedTime() < DelayRecompilation) {
+    return false;
+  }
+
+  const int size = RecompilationSchedule::length();
+  int i = 0;
+  int count = 0;
+  bool repeat = false;
+  for (; i < size && count < step; i++) {
+    if (!RecompilationSchedule::status_at(i)) {
+      MethodTrainingData* mtd = RecompilationSchedule::at(i);
+      if (!mtd->has_holder()) {
+        RecompilationSchedule::set_status_at(i, true);
+        continue;
+      }
+      const Method* method = mtd->holder();
+      InstanceKlass* klass = method->method_holder();
+      if (klass->is_not_initialized()) {
+        repeat = true;
+        continue;
+      }
+      nmethod *nm = method->code();
+      if (nm == nullptr) {
+        repeat = true;
+        continue;
+      }
+
+      if (!ForceRecompilation && !(nm->is_scc() && nm->comp_level() == CompLevel_full_optimization)) {
+        // If it's already online-compiled at level 4, mark it as done.
+        if (nm->comp_level() == CompLevel_full_optimization) {
+          RecompilationSchedule::set_status_at(i, true);
+        } else {
+          repeat = true;
+        }
+        continue;
+      }
+      if (RecompilationSchedule::claim_at(i)) {
+        const methodHandle m(THREAD, const_cast<Method*>(method));
+        CompLevel next_level = CompLevel_full_optimization;
+
+        if (method->method_data() == nullptr) {
+          CompilationPolicy::create_mdo(m, THREAD);
+        }
+
+        if (PrintTieredEvents) {
+          CompilationPolicy::print_event(CompilationPolicy::FORCE_RECOMPILE, m(), m(), InvocationEntryBci, next_level);
+        }
+        CompileBroker::compile_method(m, InvocationEntryBci, CompLevel_full_optimization, methodHandle(), 0,
+                                      true /*requires_online_compilation*/, CompileTask::Reason_MustBeCompiled, THREAD);
+        if (HAS_PENDING_EXCEPTION) {
+          CLEAR_PENDING_EXCEPTION;
+        }
+        count++;
+      }
+    }
+  }
+
+  if (i == size && !repeat) {
+    Atomic::release_store(&_recompilation_done, true);
+  }
+  return count > 0;
+}
diff a/src/hotspot/share/compiler/recompilationPolicy.hpp b/src/hotspot/share/compiler/recompilationPolicy.hpp
--- /dev/null
+++ b/src/hotspot/share/compiler/recompilationPolicy.hpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+#ifndef SHARE_COMPILER_RECOMPILATIONPOLICY_HPP
+#define SHARE_COMPILER_RECOMPILATIONPOLICY_HPP
+
+#include "compiler/compileBroker.hpp"
+#include "memory/allStatic.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+namespace CompilationPolicyUtils {
+template<int SAMPLE_COUNT = 256>
+class WeightedMovingAverage {
+  int _current;
+  int _samples[SAMPLE_COUNT];
+  int64_t _timestamps[SAMPLE_COUNT];
+
+  void sample(int s, int64_t t) {
+    assert(s >= 0, "Negative sample values are not supported");
+    _samples[_current] = s;
+    _timestamps[_current] = t;
+    if (++_current >= SAMPLE_COUNT) {
+      _current = 0;
+    }
+  }
+
+  // Since sampling happens at irregular invervals the solution is to
+  // discount the older samples proportionally to the time between
+  // the now and the time of the sample.
+  double value(int64_t t) const {
+    double decay_speed = 1;
+    double weighted_sum = 0;
+    int count = 0;
+    for (int i = 0; i < SAMPLE_COUNT; i++) {
+      if (_samples[i] >= 0) {
+        count++;
+        double delta_t = (t - _timestamps[i]) / 1000.0; // in seconds
+        if (delta_t < 1) delta_t = 1;
+        weighted_sum += (double) _samples[i] / (delta_t * decay_speed);
+      }
+    }
+    if (count > 0) {
+      return weighted_sum / count;
+    } else {
+      return 0;
+    }
+  }
+  static int64_t time() {
+    return nanos_to_millis(os::javaTimeNanos());
+  }
+public:
+  WeightedMovingAverage() : _current(0) {
+    for (int i = 0; i < SAMPLE_COUNT; i++) {
+      _samples[i] = -1;
+    }
+  }
+  void sample(int s) { sample(s, time()); }
+  double value() const { return value(time()); }
+};
+} // namespace CompilationPolicyUtils
+
+
+class RecompilationPolicy : AllStatic {
+  typedef CompilationPolicyUtils::WeightedMovingAverage<> LoadAverage;
+  static LoadAverage _load_average;
+  static volatile bool _recompilation_done;
+public:
+  static void sample_load_average();
+  static void print_load_average();
+  static bool have_recompilation_work();
+  static bool recompilation_step(int step, TRAPS);
+};
+
+#endif // SHARE_COMPILER_RECOMPILATIONPOLICY_HPP
diff a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp
--- a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp
+++ b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp
@@ -23,10 +23,13 @@
  */
 
 #include "precompiled.hpp"
 #include "c1/c1_LIRGenerator.hpp"
 #include "c1/c1_CodeStubs.hpp"
+#if INCLUDE_CDS
+#include "code/SCCache.hpp"
+#endif
 #include "gc/g1/c1/g1BarrierSetC1.hpp"
 #include "gc/g1/g1BarrierSet.hpp"
 #include "gc/g1/g1BarrierSetAssembler.hpp"
 #include "gc/g1/g1HeapRegion.hpp"
 #include "gc/g1/g1ThreadLocalData.hpp"
@@ -151,24 +154,61 @@
   }
   assert(addr->is_register(), "must be a register at this point");
 
   LIR_Opr xor_res = gen->new_pointer_register();
   LIR_Opr xor_shift_res = gen->new_pointer_register();
+#if INCLUDE_CDS
+  // we need to load the grain shift from the AOT Runtime
+  // Constants Area
+  LIR_Opr grain_shift_addr = LIR_OprFact::intptrConst(AOTRuntimeConstants::grain_shift_address());
+  LIR_Opr grain_shift_reg = gen->new_pointer_register();
+  LIR_Address* grain_shift_indirect = new LIR_Address(grain_shift_reg, 0, T_INT);
+#ifdef X86
+  LIR_Opr grain_shift = gen->shiftCountOpr();
+#else // X86
+  LIR_Opr grain_shift = gen->new_register(T_INT);
+#endif // X86
+#endif
   if (two_operand_lir_form) {
     __ move(addr, xor_res);
     __ logical_xor(xor_res, new_val, xor_res);
-    __ move(xor_res, xor_shift_res);
-    __ unsigned_shift_right(xor_shift_res,
-                            LIR_OprFact::intConst(checked_cast<jint>(G1HeapRegion::LogOfHRGrainBytes)),
-                            xor_shift_res,
-                            LIR_Opr::illegalOpr());
+#if INCLUDE_CDS
+    if (SCCache::is_on_for_write()) {
+      __ move(grain_shift_addr, grain_shift_reg);
+      __ move(xor_res, xor_shift_res);
+      __ move(grain_shift_indirect, grain_shift);
+      __ unsigned_shift_right(xor_shift_res,
+                              grain_shift,
+                              xor_shift_res,
+                              LIR_Opr::illegalOpr());
+    } else
+#endif
+    {
+      __ move(xor_res, xor_shift_res);
+      __ unsigned_shift_right(xor_shift_res,
+                              LIR_OprFact::intConst(checked_cast<jint>(G1HeapRegion::LogOfHRGrainBytes)),
+                              xor_shift_res,
+                              LIR_Opr::illegalOpr());
+    }
   } else {
     __ logical_xor(addr, new_val, xor_res);
-    __ unsigned_shift_right(xor_res,
-                            LIR_OprFact::intConst(checked_cast<jint>(G1HeapRegion::LogOfHRGrainBytes)),
-                            xor_shift_res,
-                            LIR_Opr::illegalOpr());
+#if INCLUDE_CDS
+    if (SCCache::is_on_for_write()) {
+      __ move(grain_shift_addr, grain_shift_reg);
+      __ move(grain_shift_indirect, grain_shift);
+      __ unsigned_shift_right(xor_res,
+                              grain_shift,
+                              xor_shift_res,
+                              LIR_Opr::illegalOpr());
+    } else
+#endif
+    {
+      __ unsigned_shift_right(xor_res,
+                              LIR_OprFact::intConst(checked_cast<jint>(G1HeapRegion::LogOfHRGrainBytes)),
+                              xor_shift_res,
+                              LIR_Opr::illegalOpr());
+    }
   }
 
   __ cmp(lir_cond_notEqual, xor_shift_res, LIR_OprFact::intptrConst(NULL_WORD));
 
   CodeStub* slow = new G1PostBarrierStub(addr, new_val);
diff a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp
--- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp
+++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp
@@ -22,10 +22,13 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "classfile/javaClasses.hpp"
+#if INCLUDE_CDS
+#include "code/SCCache.hpp"
+#endif
 #include "code/vmreg.inline.hpp"
 #include "gc/g1/c2/g1BarrierSetC2.hpp"
 #include "gc/g1/g1BarrierSet.hpp"
 #include "gc/g1/g1BarrierSetAssembler.hpp"
 #include "gc/g1/g1BarrierSetRuntime.hpp"
diff a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp
--- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp
+++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp
@@ -24,10 +24,11 @@
 
 #ifndef SHARE_GC_G1_G1BARRIERSET_HPP
 #define SHARE_GC_G1_G1BARRIERSET_HPP
 
 #include "gc/g1/g1DirtyCardQueue.hpp"
+#include "gc/g1/g1HeapRegion.hpp"
 #include "gc/g1/g1SATBMarkQueueSet.hpp"
 #include "gc/shared/cardTable.hpp"
 #include "gc/shared/cardTableBarrierSet.hpp"
 #include "gc/shared/bufferNode.hpp"
 
@@ -89,10 +90,12 @@
 
   static G1DirtyCardQueueSet& dirty_card_queue_set() {
     return g1_barrier_set()->_dirty_card_queue_set;
   }
 
+  virtual uint grain_shift() { return G1HeapRegion::LogOfHRGrainBytes; }
+
   // Callbacks for runtime accesses.
   template <DecoratorSet decorators, typename BarrierSetT = G1BarrierSet>
   class AccessBarrier: public ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT> {
     typedef ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT> ModRef;
     typedef BarrierSet::AccessBarrier<decorators, BarrierSetT> Raw;
diff a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp
--- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp
+++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp
@@ -176,10 +176,11 @@
   assert(cb != nullptr, "invariant");
 
   nmethod* nm = cb->as_nmethod();
   BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
 
+  log_trace(nmethod, barrier)("Running nmethod entry barrier: %d " PTR_FORMAT, nm->compile_id(), p2i(nm));
   // Called upon first entry after being armed
   bool may_enter = bs_nm->nmethod_entry_barrier(nm);
   assert(!nm->is_osr_method() || may_enter, "OSR nmethods should always be entrant after migration");
 
   // In case a concurrent thread disarmed the nmethod, we need to ensure the new instructions
@@ -197,19 +198,24 @@
     if (Atomic::add(&counter, 1u) % 10 == 0) {
       may_enter = false;
     }
   }
 
-  if (!may_enter) {
+  if (may_enter) {
+    nm->set_used();
+  } else {
     log_trace(nmethod, barrier)("Deoptimizing nmethod: " PTR_FORMAT, p2i(nm));
     bs_nm->deoptimize(nm, return_address_ptr);
   }
   return may_enter ? 0 : 1;
 }
 
 bool BarrierSetNMethod::nmethod_osr_entry_barrier(nmethod* nm) {
   assert(nm->is_osr_method(), "Should not reach here");
-  log_trace(nmethod, barrier)("Running osr nmethod entry barrier: " PTR_FORMAT, p2i(nm));
+  log_trace(nmethod, barrier)("Running osr nmethod entry barrier: %d " PTR_FORMAT, nm->compile_id(), p2i(nm));
   bool result = nmethod_entry_barrier(nm);
+  if (result) {
+    nm->set_used();
+  }
   OrderAccess::cross_modify_fence();
   return result;
 }
diff a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp
--- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp
+++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp
@@ -104,10 +104,15 @@
 
   virtual void make_parsable(JavaThread* thread) { flush_deferred_card_mark_barrier(thread); }
 
   virtual void print_on(outputStream* st) const;
 
+  // The AOT code cache manager needs to know the current card shift
+  // and, for some barrier sets, the region grain size shift
+  uint card_shift() const { return _card_table->card_shift(); }
+  virtual uint grain_shift() { return 0; }
+
   template <DecoratorSet decorators, typename BarrierSetT = CardTableBarrierSet>
   class AccessBarrier: public ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT> {};
 };
 
 template<>
diff a/src/hotspot/share/gc/shared/collectorCounters.cpp b/src/hotspot/share/gc/shared/collectorCounters.cpp
--- a/src/hotspot/share/gc/shared/collectorCounters.cpp
+++ b/src/hotspot/share/gc/shared/collectorCounters.cpp
@@ -65,11 +65,11 @@
 CollectorCounters::~CollectorCounters() {
   FREE_C_HEAP_ARRAY(char, _name_space);
 }
 
 TraceCollectorStats::TraceCollectorStats(CollectorCounters* c) :
-    PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()),
+    PerfTraceElapsedTimeEvent(c->time_counter(), c->invocation_counter()),
     _c(c) {
 
   if (UsePerfData) {
      _c->last_entry_counter()->set_value(os::elapsed_counter());
   }
diff a/src/hotspot/share/gc/shared/collectorCounters.hpp b/src/hotspot/share/gc/shared/collectorCounters.hpp
--- a/src/hotspot/share/gc/shared/collectorCounters.hpp
+++ b/src/hotspot/share/gc/shared/collectorCounters.hpp
@@ -60,11 +60,11 @@
     inline PerfVariable* last_exit_counter() const  { return _last_exit_time; }
 
     const char* name_space() const                  { return _name_space; }
 };
 
-class TraceCollectorStats: public PerfTraceTimedEvent {
+class TraceCollectorStats: public PerfTraceElapsedTimeEvent {
 
   protected:
     CollectorCounters* _c;
 
   public:
diff a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp
--- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp
+++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp
@@ -187,11 +187,14 @@
     return "shenandoah_cas_obj";
   }
 #endif // PRODUCT
 };
 
+class SCAddressTable;
+
 class ShenandoahBarrierSetC1 : public BarrierSetC1 {
+  friend class SCAddressTable;
 private:
   CodeBlob* _pre_barrier_c1_runtime_code_blob;
   CodeBlob* _load_reference_barrier_strong_rt_code_blob;
   CodeBlob* _load_reference_barrier_strong_native_rt_code_blob;
   CodeBlob* _load_reference_barrier_weak_rt_code_blob;
diff a/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp b/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp
--- a/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp
+++ b/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp
@@ -85,11 +85,14 @@
 #ifndef PRODUCT
   virtual void print_name(outputStream* out) const;
 #endif // PRODUCT
 };
 
+class SCAddressTable;
+
 class ZBarrierSetC1 : public BarrierSetC1 {
+  friend SCAddressTable;
 private:
   address _load_barrier_on_oop_field_preloaded_runtime_stub;
   address _load_barrier_on_weak_oop_field_preloaded_runtime_stub;
   address _store_barrier_on_oop_field_with_healing;
   address _store_barrier_on_oop_field_without_healing;
diff a/src/hotspot/share/include/cds.h b/src/hotspot/share/include/cds.h
--- a/src/hotspot/share/include/cds.h
+++ b/src/hotspot/share/include/cds.h
@@ -33,15 +33,15 @@
 // We should use only standard C types. Do not use custom types such as bool, intx,
 // etc, to avoid introducing unnecessary dependencies to other HotSpot type declarations.
 //
 // Also, this is a C header file. Do not use C++ here.
 
-#define NUM_CDS_REGIONS 4 // this must be the same as MetaspaceShared::n_regions
+#define NUM_CDS_REGIONS 5 // this must be the same as MetaspaceShared::n_regions
 #define CDS_ARCHIVE_MAGIC 0xf00baba2
 #define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8
 #define CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION 13
-#define CURRENT_CDS_ARCHIVE_VERSION 18
+#define CURRENT_CDS_ARCHIVE_VERSION 19
 
 typedef struct CDSFileMapRegion {
   int     _crc;               // CRC checksum of this region.
   int     _read_only;         // read only region?
   int     _allow_exec;        // executable code in this region?
diff a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -85,10 +85,16 @@
 JVM_InternString(JNIEnv *env, jstring str);
 
 /*
  * java.lang.System
  */
+JNIEXPORT jboolean JNICALL
+JVM_AOTIsTraining(JNIEnv *env);
+
+JNIEXPORT void JNICALL
+JVM_AOTEndTraining(JNIEnv *env);
+
 JNIEXPORT jlong JNICALL
 JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored);
 
 JNIEXPORT jlong JNICALL
 JVM_NanoTime(JNIEnv *env, jclass ignored);
@@ -210,10 +216,13 @@
 JVM_GetRandomSeedForDumping();
 
 JNIEXPORT void JNICALL
 JVM_LogLambdaFormInvoker(JNIEnv* env, jstring line);
 
+JNIEXPORT void JNICALL
+JVM_LogDynamicProxy(JNIEnv *env, jobject loader, jstring proxy_name, jobjectArray interfaces, jint accessFlags);
+
 JNIEXPORT void JNICALL
 JVM_DumpClassListToFile(JNIEnv* env, jstring fileName);
 
 JNIEXPORT void JNICALL
 JVM_DumpDynamicArchive(JNIEnv* env, jstring archiveName);
diff a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp
--- a/src/hotspot/share/interpreter/abstractInterpreter.cpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp
@@ -169,21 +169,24 @@
     if (m->is_continuation_native_intrinsic()) {
       // This entry will never be called.  The real entry gets generated later, like for MH intrinsics.
       return abstract;
     }
     assert(!m->is_method_handle_intrinsic(), "overlapping bits here, watch out");
-    return m->is_synchronized() ? native_synchronized : native;
+    if (m->is_synchronized()) {
+      return m->has_upcall_on_method_entry() ? native_synchronized_upcalls : native_synchronized;
+    }
+    return m->has_upcall_on_method_entry() ? native_upcalls : native;
   }
 
   // Synchronized?
   if (m->is_synchronized()) {
-    return zerolocals_synchronized;
+    return m->has_upcall_on_method_entry() ? zerolocals_synchronized_upcalls : zerolocals_synchronized;
   }
 
   // Empty method?
   if (m->is_empty_method()) {
-    return empty;
+    return m->has_upcall_on_method_entry() ? empty_upcalls : empty;
   }
 
   // Getter method?
   if (m->is_getter()) {
     return getter;
@@ -193,11 +196,11 @@
   if (m->is_setter()) {
     return setter;
   }
 
   // Note: for now: zero locals for all non-empty methods
-  return zerolocals;
+  return m->has_upcall_on_method_entry() ? zerolocals_upcalls : zerolocals;
 }
 
 vmIntrinsics::ID AbstractInterpreter::method_intrinsic(MethodKind kind) {
   switch (kind) {
   case java_lang_math_sin         : return vmIntrinsics::_dsin;
diff a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp
--- a/src/hotspot/share/interpreter/abstractInterpreter.hpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp
@@ -57,13 +57,18 @@
   friend class TemplateInterpreterGenerator;
  public:
   enum MethodKind {
     zerolocals,                                                 // method needs locals initialization
     zerolocals_synchronized,                                    // method needs locals initialization & is synchronized
+    zerolocals_upcalls,                                         // method needs locals initialization & has runtime upcalls
+    zerolocals_synchronized_upcalls,                            // method needs locals initialization & is synchronized & has runtime upcalls
     native,                                                     // native method
     native_synchronized,                                        // native method & is synchronized
+    native_upcalls,                                             // native method & has runtime upcalls
+    native_synchronized_upcalls,                                // native method & is synchronized & has runtime upcalls
     empty,                                                      // empty method (code: _return)
+    empty_upcalls,                                              // empty method & has runtime upcalls (code: _return)
     getter,                                                     // getter method
     setter,                                                     // setter method
     abstract,                                                   // abstract method (throws an AbstractMethodException)
     method_handle_invoke_FIRST,                                 // java.lang.invoke.MethodHandles::invokeExact, etc.
     method_handle_invoke_LAST                                   = (method_handle_invoke_FIRST
diff a/src/hotspot/share/interpreter/bytecodeHistogram.cpp b/src/hotspot/share/interpreter/bytecodeHistogram.cpp
--- a/src/hotspot/share/interpreter/bytecodeHistogram.cpp
+++ b/src/hotspot/share/interpreter/bytecodeHistogram.cpp
@@ -27,61 +27,50 @@
 #include "memory/resourceArea.hpp"
 #include "runtime/os.hpp"
 #include "utilities/growableArray.hpp"
 
 // ------------------------------------------------------------------------------------------------
-// Non-product code
-#ifndef PRODUCT
 
 // Implementation of BytecodeCounter
 
-int   BytecodeCounter::_counter_value = 0;
+jlong BytecodeCounter::_counter_value = 0;
 jlong BytecodeCounter::_reset_time    = 0;
 
-
 void BytecodeCounter::reset() {
   _counter_value = 0;
   _reset_time    = os::elapsed_counter();
 }
 
-
 double BytecodeCounter::elapsed_time() {
   return (double)(os::elapsed_counter() - _reset_time) / (double)os::elapsed_frequency();
 }
 
-
 double BytecodeCounter::frequency() {
   return (double)counter_value() / elapsed_time();
 }
 
-
 void BytecodeCounter::print() {
-  tty->print_cr(
-    "%d bytecodes executed in %.1fs (%.3fMHz)",
-    counter_value(),
-    elapsed_time(),
-    frequency() / 1000000.0
-  );
+  tty->print_cr(JLONG_FORMAT " bytecodes executed in %.1fs (%.3fMHz)",
+                counter_value(), elapsed_time(), frequency() / 1000000.0);
 }
 
 
 // Helper class for sorting
 
 class HistoEntry: public ResourceObj {
- private:
+private:
   int             _index;
   int             _count;
 
- public:
+public:
   HistoEntry(int index, int count)                         { _index = index; _count = count; }
   int             index() const                            { return _index; }
   int             count() const                            { return _count; }
 
   static int      compare(HistoEntry** x, HistoEntry** y)  { return (*x)->count() - (*y)->count(); }
 };
 
-
 // Helper functions
 
 static GrowableArray<HistoEntry*>* sorted_array(int* array, int length) {
   GrowableArray<HistoEntry*>* a = new GrowableArray<HistoEntry*>(length);
   int i = length;
@@ -140,10 +129,12 @@
   float rel_sum = (float)abs_sum * 100.0F / (float)tot;
   tty->print_cr("%10d  %7.2f%%    (cutoff = %.2f%%)", abs_sum, rel_sum, cutoff);
   tty->cr();
 }
 
+// Non-product code
+#ifndef PRODUCT
 
 // Implementation of BytecodePairHistogram
 
 int BytecodePairHistogram::_index;
 int BytecodePairHistogram::_counters[BytecodePairHistogram::number_of_pairs];
diff a/src/hotspot/share/interpreter/bytecodeHistogram.hpp b/src/hotspot/share/interpreter/bytecodeHistogram.hpp
--- a/src/hotspot/share/interpreter/bytecodeHistogram.hpp
+++ b/src/hotspot/share/interpreter/bytecodeHistogram.hpp
@@ -30,45 +30,45 @@
 
 // BytecodeCounter counts the number of bytecodes executed
 
 class BytecodeCounter: AllStatic {
  private:
-  NOT_PRODUCT(static int   _counter_value;)
-  NOT_PRODUCT(static jlong _reset_time;)
+  static jlong _counter_value;
+  static jlong _reset_time;
 
   friend class TemplateInterpreterGenerator;
   friend class         BytecodeInterpreter;
 
  public:
   // Initialization
-  static void reset()                      PRODUCT_RETURN;
+  static void reset();
 
   // Counter info (all info since last reset)
-  static int    counter_value()            PRODUCT_RETURN0 NOT_PRODUCT({ return _counter_value; });
-  static double elapsed_time()             PRODUCT_RETURN0; // in seconds
-  static double frequency()                PRODUCT_RETURN0; // bytecodes/seconds
+  static jlong  counter_value() { return _counter_value; }
+  static double elapsed_time(); // in seconds
+  static double frequency();    // bytecodes/seconds
 
   // Counter printing
-  static void   print()                    PRODUCT_RETURN;
+  static void   print();
 };
 
 
 // BytecodeHistogram collects number of executions of bytecodes
 
 class BytecodeHistogram: AllStatic {
  private:
-  NOT_PRODUCT(static int _counters[Bytecodes::number_of_codes];)   // a counter for each bytecode
+  static int _counters[Bytecodes::number_of_codes];   // a counter for each bytecode
 
   friend class TemplateInterpreterGenerator;
   friend class         BytecodeInterpreter;
 
  public:
   // Initialization
-  static void reset()                       PRODUCT_RETURN; // reset counters
+  static void reset(); // reset counters
 
   // Profile printing
-  static void print(float cutoff = 0.01F)   PRODUCT_RETURN; // cutoff in percent
+  static void print(float cutoff); // cutoff in percent
 };
 
 
 // BytecodePairHistogram collects number of executions of bytecode pairs.
 // A bytecode pair is any sequence of two consecutive bytecodes.
diff a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp
--- a/src/hotspot/share/interpreter/bytecodeTracer.cpp
+++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/heapShared.hpp"
 #include "classfile/classPrinter.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "interpreter/bytecodeHistogram.hpp"
 #include "interpreter/bytecodeStream.hpp"
 #include "interpreter/bytecodeTracer.hpp"
@@ -128,14 +129,14 @@
         code == Bytecodes::_return_register_finalizer ||
         (code >= Bytecodes::_ireturn && code <= Bytecodes::_return)) {
       int bci = (int)(bcp - method->code_base());
       st->print("[" UINTX_FORMAT  "] ", Thread::current()->osthread()->thread_id_for_printing());
       if (Verbose) {
-        st->print("%8d  %4d  " INTPTR_FORMAT " " INTPTR_FORMAT " %s",
+        st->print(JLONG_FORMAT_W(8) "  %4d  " INTPTR_FORMAT " " INTPTR_FORMAT " %s",
             BytecodeCounter::counter_value(), bci, tos, tos2, Bytecodes::name(code));
       } else {
-        st->print("%8d  %4d  %s",
+        st->print(JLONG_FORMAT_W(8) "  %4d  %s",
             BytecodeCounter::counter_value(), bci, Bytecodes::name(code));
       }
       print_attributes(bci, st);
     }
     // Set is_wide for the next one, since the caller of this doesn't skip
@@ -173,11 +174,13 @@
     } else {
       st->print("%4d %s", bci, Bytecodes::name(code));
     }
     _next_pc = is_wide() ? bcp+2 : bcp+1;
     print_attributes(bci, st);
-    bytecode_epilog(bci, st);
+    if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_PROFILE)) {
+      bytecode_epilog(bci, st);
+    }
   }
 };
 
 // We need a global instance to keep track of the states when the bytecodes
 // are executed. Access by multiple threads are controlled by ttyLocker.
@@ -300,10 +303,16 @@
 
     if (is_linked()) {
       ResolvedIndyEntry* indy_entry = constants()->resolved_indy_entry_at(indy_index);
       st->print("  ResolvedIndyEntry: ");
       indy_entry->print_on(st);
+      if (indy_entry->has_appendix()) {
+        oop apx = constants()->resolved_reference_from_indy(indy_index);
+        //FIXME: lock out of order with runtime/interpreter/BytecodeTracerTest.java
+        //int perm_index = HeapShared::get_archived_object_permanent_index(apx);
+        st->print_cr(" - appendix = " INTPTR_FORMAT, p2i(apx));
+      }
     }
   }
 }
 
 // cp_index: must be the cp_index of a JVM_CONSTANT_{Dynamic, DynamicInError, InvokeDynamic}
diff a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp
@@ -66,15 +66,17 @@
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/jfieldIDWorkaround.hpp"
 #include "runtime/osThread.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stackWatermarkSet.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/synchronizer.inline.hpp"
 #include "runtime/threadCritical.hpp"
+#include "services/management.hpp"
 #include "utilities/align.hpp"
 #include "utilities/checkedCast.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/events.hpp"
 #ifdef COMPILER2
@@ -121,10 +123,127 @@
   }
 
   frame& get_frame()                             { return _last_frame; }
 };
 
+static bool is_resolved(JavaThread* current) {
+  LastFrameAccessor last_frame(current);
+  ConstantPool* constants = last_frame.method()->constants();
+  Bytecodes::Code bc = last_frame.code();
+
+  if (bc == Bytecodes::_ldc || bc == Bytecodes::_ldc_w || bc == Bytecodes::_ldc2_w ||
+      bc == Bytecodes::_fast_aldc || bc == Bytecodes::_fast_aldc_w) {
+    bool is_wide = (bc != Bytecodes::_ldc) && (bc != Bytecodes::_fast_aldc);
+    int index = (is_wide ? last_frame.get_index_u1(bc) : last_frame.get_index_u2(bc));
+    constantTag tag = constants->tag_at(index);
+    assert(tag.is_klass_or_reference(), "unknown tag: %s", tag.internal_name());
+    return constants->tag_at(index).is_klass();
+  } else if (bc == Bytecodes::_invokedynamic) {
+    int index = last_frame.get_index_u4(bc);
+    int indy_index = index;
+    ResolvedIndyEntry* indy_entry = constants->resolved_indy_entry_at(indy_index);
+    return indy_entry->is_resolved();
+  } else if (Bytecodes::is_invoke(bc)) {
+    int index = last_frame.get_index_u2(bc);
+    ResolvedMethodEntry* rme = constants->resolved_method_entry_at(index);
+    return rme->is_resolved(bc);
+  } else if (Bytecodes::is_field_code(bc) || bc == Bytecodes::_nofast_getfield || bc == Bytecodes::_nofast_putfield) {
+    if (bc == Bytecodes::_nofast_getfield) {
+      bc = Bytecodes::_getfield;
+    } else if (bc == Bytecodes::_nofast_putfield) {
+      bc = Bytecodes::_putfield;
+    }
+    int index = last_frame.get_index_u2(bc);
+    ResolvedFieldEntry* field_entry = constants->cache()->resolved_field_entry_at(index);
+    return field_entry->is_resolved(bc);
+  } else if (bc == Bytecodes::_new) {
+    int index = last_frame.get_index_u2(bc);
+    constantTag tag = constants->tag_at(index);
+    assert(tag.is_klass_or_reference(), "unknown tag: %s", tag.internal_name());
+    return constants->tag_at(index).is_klass();
+  }
+  return false;
+}
+
+static void trace_current_location(JavaThread* current) {
+  LogStreamHandle(Debug, init, interpreter) log;
+  if (current->profile_rt_calls() && log.is_enabled()) {
+    ResourceMark rm(current);
+    LastFrameAccessor last_frame(current);
+    Method* caller = last_frame.method();
+    ConstantPool* constants = caller->constants();
+    Bytecodes::Code bc = last_frame.code();
+    log.print("InterpreterRuntime: " INTPTR_FORMAT ": %s: " INTPTR_FORMAT,
+              p2i(current), Bytecodes::name(bc), p2i(caller));
+    if (caller->is_shared()) {
+      log.print(" shared");
+    }
+    if (is_resolved(current)) {
+      log.print(" resolved");
+    }
+    log.print(" ");
+    caller->print_short_name(&log);
+    log.print(" @ %d:", last_frame.bci());
+    int instruction_size = last_frame.bytecode().instruction_size();
+
+    if (Bytecodes::is_invoke(bc) && bc != Bytecodes::_invokedynamic) {
+      int index = last_frame.get_index_u2(bc);
+      ResolvedMethodEntry* rme = constants->resolved_method_entry_at(index);
+      if (rme->is_resolved(bc)) {
+        Method* m = rme->method();
+        if (m != nullptr) {
+          log.print(" %s", m->method_holder()->init_state_name());
+        } else {
+          log.print(" null");
+        }
+      }
+    } else if (Bytecodes::is_field_code(bc) || bc == Bytecodes::_nofast_getfield || bc == Bytecodes::_nofast_putfield) {
+      if (bc == Bytecodes::_nofast_getfield) {
+        bc = Bytecodes::_getfield;
+      } else if (bc == Bytecodes::_nofast_putfield) {
+        bc = Bytecodes::_putfield;
+      }
+      int index = last_frame.get_index_u2(bc);
+      ResolvedFieldEntry* field_entry = constants->cache()->resolved_field_entry_at(index);
+
+      if (field_entry->is_resolved(bc)) {
+        log.print(" %s", field_entry->field_holder()->init_state_name());
+      }
+    } else if (bc == Bytecodes::_new) {
+      int index = last_frame.get_index_u2(bc);
+      constantTag tag = constants->tag_at(index);
+      assert(tag.is_klass_or_reference(), "unknown tag: %s", tag.internal_name());
+      if (constants->tag_at(index).is_klass()) {
+        CPKlassSlot kslot = constants->klass_slot_at(index);
+        int resolved_klass_index = kslot.resolved_klass_index();
+        Klass* k = constants->resolved_klasses()->at(resolved_klass_index);
+        log.print(": %s", InstanceKlass::cast(k)->init_state_name());
+      }
+    }
+    log.print(" ");
+    caller->print_codes_on(last_frame.bci(), last_frame.bci() + instruction_size, &log, /*flags*/ 0);
+
+    LogStreamHandle(Trace, init, interpreter) log1;
+    if (log1.is_enabled()) {
+      if (bc == Bytecodes::_invokedynamic) {
+        int index = last_frame.get_index_u4(bc);
+        int indy_index = index;
+        ResolvedIndyEntry* indy_entry = constants->resolved_indy_entry_at(indy_index);
+        indy_entry->print_on(&log1);
+      } else if (Bytecodes::is_invoke(bc)) {
+        int index = last_frame.get_index_u2(bc);
+        ResolvedMethodEntry* rme = constants->resolved_method_entry_at(index);
+        rme->print_on(&log1);
+      } else if (Bytecodes::is_field_code(bc) || bc == Bytecodes::_nofast_getfield || bc == Bytecodes::_nofast_putfield) {
+        int index = last_frame.get_index_u2(bc);
+        ResolvedFieldEntry* field_entry = constants->cache()->resolved_field_entry_at(index);
+        field_entry->print_on(&log1);
+      }
+    }
+  }
+}
+
 //------------------------------------------------------------------------------------------------------------------------
 // State accessors
 
 void InterpreterRuntime::set_bcp_and_mdp(address bcp, JavaThread* current) {
   LastFrameAccessor last_frame(current);
@@ -142,11 +261,11 @@
 
 //------------------------------------------------------------------------------------------------------------------------
 // Constants
 
 
-JRT_ENTRY(void, InterpreterRuntime::ldc(JavaThread* current, bool wide))
+JRT_ENTRY_PROF(void, InterpreterRuntime, ldc, InterpreterRuntime::ldc(JavaThread* current, bool wide))
   // access constant pool
   LastFrameAccessor last_frame(current);
   ConstantPool* pool = last_frame.method()->constants();
   int cp_index = wide ? last_frame.get_index_u2(Bytecodes::_ldc_w) : last_frame.get_index_u1(Bytecodes::_ldc);
   constantTag tag = pool->tag_at(cp_index);
@@ -155,11 +274,11 @@
   Klass* klass = pool->klass_at(cp_index, CHECK);
   oop java_class = klass->java_mirror();
   current->set_vm_result(java_class);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::resolve_ldc(JavaThread* current, Bytecodes::Code bytecode)) {
+JRT_ENTRY_PROF(void, InterpreterRuntime, resolve_ldc, InterpreterRuntime::resolve_ldc(JavaThread* current, Bytecodes::Code bytecode)) {
   assert(bytecode == Bytecodes::_ldc ||
          bytecode == Bytecodes::_ldc_w ||
          bytecode == Bytecodes::_ldc2_w ||
          bytecode == Bytecodes::_fast_aldc ||
          bytecode == Bytecodes::_fast_aldc_w, "wrong bc");
@@ -211,11 +330,11 @@
 
 
 //------------------------------------------------------------------------------------------------------------------------
 // Allocation
 
-JRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* current, ConstantPool* pool, int index))
+JRT_ENTRY_PROF(void, InterpreterRuntime, new, InterpreterRuntime::_new(JavaThread* current, ConstantPool* pool, int index))
   Klass* k = pool->klass_at(index, CHECK);
   InstanceKlass* klass = InstanceKlass::cast(k);
 
   // Make sure we are not instantiating an abstract klass
   klass->check_valid_for_instantiation(true, CHECK);
@@ -226,24 +345,24 @@
   oop obj = klass->allocate_instance(CHECK);
   current->set_vm_result(obj);
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread* current, BasicType type, jint size))
+JRT_ENTRY_PROF(void, InterpreterRuntime, newarray, InterpreterRuntime::newarray(JavaThread* current, BasicType type, jint size))
   oop obj = oopFactory::new_typeArray(type, size, CHECK);
   current->set_vm_result(obj);
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::anewarray(JavaThread* current, ConstantPool* pool, int index, jint size))
+JRT_ENTRY_PROF(void, InterpreterRuntime, anewarray, InterpreterRuntime::anewarray(JavaThread* current, ConstantPool* pool, int index, jint size))
   Klass*    klass = pool->klass_at(index, CHECK);
   objArrayOop obj = oopFactory::new_objArray(klass, size, CHECK);
   current->set_vm_result(obj);
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* current, jint* first_size_address))
+JRT_ENTRY_PROF(void, InterpreterRuntime, multianewarray, InterpreterRuntime::multianewarray(JavaThread* current, jint* first_size_address))
   // We may want to pass in more arguments - could make this slightly faster
   LastFrameAccessor last_frame(current);
   ConstantPool* constants = last_frame.method()->constants();
   int          i = last_frame.get_index_u2(Bytecodes::_multianewarray);
   Klass* klass   = constants->klass_at(i, CHECK);
@@ -267,19 +386,19 @@
   oop obj = ArrayKlass::cast(klass)->multi_allocate(nof_dims, dims, CHECK);
   current->set_vm_result(obj);
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::register_finalizer(JavaThread* current, oopDesc* obj))
+JRT_ENTRY_PROF(void, InterpreterRuntime, register_finalizer, InterpreterRuntime::register_finalizer(JavaThread* current, oopDesc* obj))
   assert(oopDesc::is_oop(obj), "must be a valid oop");
   assert(obj->klass()->has_finalizer(), "shouldn't be here otherwise");
   InstanceKlass::register_finalizer(instanceOop(obj), CHECK);
 JRT_END
 
 
 // Quicken instance-of and check-cast bytecodes
-JRT_ENTRY(void, InterpreterRuntime::quicken_io_cc(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, quicken_io_cc, InterpreterRuntime::quicken_io_cc(JavaThread* current))
   // Force resolving; quicken the bytecode
   LastFrameAccessor last_frame(current);
   int which = last_frame.get_index_u2(Bytecodes::_checkcast);
   ConstantPool* cpool = last_frame.method()->constants();
   // We'd expect to assert that we're only here to quicken bytecodes, but in a multithreaded
@@ -347,11 +466,12 @@
 
 // Special handling for stack overflow: since we don't have any (java) stack
 // space left we use the pre-allocated & pre-initialized StackOverflowError
 // klass to create an stack overflow error instance.  We do not call its
 // constructor for the same reason (it is empty, anyway).
-JRT_ENTRY(void, InterpreterRuntime::throw_StackOverflowError(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_StackOverflowError,
+           InterpreterRuntime::throw_StackOverflowError(JavaThread* current))
   Handle exception = get_preinitialized_exception(
                                  vmClasses::StackOverflowError_klass(),
                                  CHECK);
   // Increment counter for hs_err file reporting
   Atomic::inc(&Exceptions::_stack_overflow_errors);
@@ -359,11 +479,12 @@
   // while we were trying to manipulate ScopedValue bindings.
   current->clear_scopedValueBindings();
   THROW_HANDLE(exception);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::throw_delayed_StackOverflowError(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_delayed_StackOverflowError,
+           InterpreterRuntime::throw_delayed_StackOverflowError(JavaThread* current))
   Handle exception = get_preinitialized_exception(
                                  vmClasses::StackOverflowError_klass(),
                                  CHECK);
   java_lang_Throwable::set_message(exception(),
           Universe::delayed_stack_overflow_error_message());
@@ -373,11 +494,12 @@
   // while we were trying to manipulate ScopedValue bindings.
   current->clear_scopedValueBindings();
   THROW_HANDLE(exception);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::create_exception(JavaThread* current, char* name, char* message))
+JRT_ENTRY_PROF(void, InterpreterRuntime, create_exception,
+           InterpreterRuntime::create_exception(JavaThread* current, char* name, char* message))
   // lookup exception klass
   TempNewSymbol s = SymbolTable::new_symbol(name);
   if (ProfileTraps) {
     if (s == vmSymbols::java_lang_ArithmeticException()) {
       note_trap(current, Deoptimization::Reason_div0_check);
@@ -389,11 +511,12 @@
   Handle exception = Exceptions::new_exception(current, s, message);
   current->set_vm_result(exception());
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::create_klass_exception(JavaThread* current, char* name, oopDesc* obj))
+JRT_ENTRY_PROF(void, InterpreterRuntime, create_klass_exception,
+           InterpreterRuntime::create_klass_exception(JavaThread* current, char* name, oopDesc* obj))
   // Produce the error message first because note_trap can safepoint
   ResourceMark rm(current);
   const char* klass_name = obj->klass()->external_name();
   // lookup exception klass
   TempNewSymbol s = SymbolTable::new_symbol(name);
@@ -407,11 +530,12 @@
   // create exception, with klass name as detail message
   Handle exception = Exceptions::new_exception(current, s, klass_name);
   current->set_vm_result(exception());
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException(JavaThread* current, arrayOopDesc* a, jint index))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_ArrayIndexOutOfBoundsException,
+           InterpreterRuntime::throw_ArrayIndexOutOfBoundsException(JavaThread* current, arrayOopDesc* a, jint index))
   // Produce the error message first because note_trap can safepoint
   ResourceMark rm(current);
   stringStream ss;
   ss.print("Index %d out of bounds for length %d", index, a->length());
 
@@ -420,11 +544,12 @@
   }
 
   THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string());
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::throw_ClassCastException(
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_ClassCastException,
+           InterpreterRuntime::throw_ClassCastException(
   JavaThread* current, oopDesc* obj))
 
   // Produce the error message first because note_trap can safepoint
   ResourceMark rm(current);
   char* message = SharedRuntime::generate_class_cast_message(
@@ -445,11 +570,12 @@
 // During this operation, the expression stack contains the values for the
 // bci where the exception happened. If the exception was propagated back
 // from a call, the expression stack contains the values for the bci at the
 // invoke w/o arguments (i.e., as if one were inside the call).
 // Note that the implementation of this method assumes it's only called when an exception has actually occured
-JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThread* current, oopDesc* exception))
+JRT_ENTRY_PROF(address, InterpreterRuntime, exception_handler_for_exception,
+           InterpreterRuntime::exception_handler_for_exception(JavaThread* current, oopDesc* exception))
   // We get here after we have unwound from a callee throwing an exception
   // into the interpreter. Any deferred stack processing is notified of
   // the event via the StackWatermarkSet.
   StackWatermarkSet::after_unwind(current);
 
@@ -584,68 +710,89 @@
   current->set_vm_result(h_exception());
   return continuation;
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::throw_pending_exception(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_pending_exception, InterpreterRuntime::throw_pending_exception(JavaThread* current))
   assert(current->has_pending_exception(), "must only be called if there's an exception pending");
   // nothing to do - eventually we should remove this code entirely (see comments @ call sites)
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::throw_AbstractMethodError(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_AbstractMethodError, InterpreterRuntime::throw_AbstractMethodError(JavaThread* current))
   THROW(vmSymbols::java_lang_AbstractMethodError());
 JRT_END
 
 // This method is called from the "abstract_entry" of the interpreter.
 // At that point, the arguments have already been removed from the stack
 // and therefore we don't have the receiver object at our fingertips. (Though,
 // on some platforms the receiver still resides in a register...). Thus,
 // we have no choice but print an error message not containing the receiver
 // type.
-JRT_ENTRY(void, InterpreterRuntime::throw_AbstractMethodErrorWithMethod(JavaThread* current,
-                                                                        Method* missingMethod))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_AbstractMethodErrorWithMethod,
+           InterpreterRuntime::throw_AbstractMethodErrorWithMethod(JavaThread* current,
+                                                                   Method* missingMethod))
   ResourceMark rm(current);
   assert(missingMethod != nullptr, "sanity");
   methodHandle m(current, missingMethod);
   LinkResolver::throw_abstract_method_error(m, THREAD);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::throw_AbstractMethodErrorVerbose(JavaThread* current,
-                                                                     Klass* recvKlass,
-                                                                     Method* missingMethod))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_AbstractMethodErrorVerbose,
+          InterpreterRuntime::throw_AbstractMethodErrorVerbose(JavaThread* current,
+                                                               Klass* recvKlass,
+                                                               Method* missingMethod))
   ResourceMark rm(current);
   methodHandle mh = methodHandle(current, missingMethod);
   LinkResolver::throw_abstract_method_error(mh, recvKlass, THREAD);
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::throw_IncompatibleClassChangeError(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_IncompatibleClassChangeError,
+           InterpreterRuntime::throw_IncompatibleClassChangeError(JavaThread* current))
   THROW(vmSymbols::java_lang_IncompatibleClassChangeError());
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose(JavaThread* current,
-                                                                              Klass* recvKlass,
-                                                                              Klass* interfaceKlass))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_IncompatibleClassChangeErrorVerbose,
+           InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose(JavaThread* current,
+                                                                         Klass* recvKlass,
+                                                                         Klass* interfaceKlass))
   ResourceMark rm(current);
   char buf[1000];
   buf[0] = '\0';
   jio_snprintf(buf, sizeof(buf),
                "Class %s does not implement the requested interface %s",
                recvKlass ? recvKlass->external_name() : "nullptr",
                interfaceKlass ? interfaceKlass->external_name() : "nullptr");
   THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::throw_NullPointerException(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_NullPointerException,
+           InterpreterRuntime::throw_NullPointerException(JavaThread* current))
   THROW(vmSymbols::java_lang_NullPointerException());
 JRT_END
 
 //------------------------------------------------------------------------------------------------------------------------
 // Fields
 //
 
+PROF_ENTRY(void, InterpreterRuntime, resolve_getfield, InterpreterRuntime::resolve_getfield(JavaThread* current))
+  resolve_get_put(current, Bytecodes::_getfield);
+PROF_END
+
+PROF_ENTRY(void, InterpreterRuntime, resolve_putfield, InterpreterRuntime::resolve_putfield(JavaThread* current))
+  resolve_get_put(current, Bytecodes::_putfield);
+PROF_END
+
+PROF_ENTRY(void, InterpreterRuntime, resolve_getstatic, InterpreterRuntime::resolve_getstatic(JavaThread* current))
+  resolve_get_put(current, Bytecodes::_getstatic);
+PROF_END
+
+PROF_ENTRY(void, InterpreterRuntime, resolve_putstatic, InterpreterRuntime::resolve_putstatic(JavaThread* current))
+  resolve_get_put(current, Bytecodes::_putstatic);
+PROF_END
+
 void InterpreterRuntime::resolve_get_put(JavaThread* current, Bytecodes::Code bytecode) {
   LastFrameAccessor last_frame(current);
   constantPoolHandle pool(current, last_frame.method()->constants());
   methodHandle m(current, last_frame.method());
 
@@ -699,11 +846,14 @@
                                       info.has_initialized_final_update();
   assert(!(has_initialized_final_update && !info.access_flags().is_final()), "Fields with initialized final updates must be final");
 
   Bytecodes::Code get_code = (Bytecodes::Code)0;
   Bytecodes::Code put_code = (Bytecodes::Code)0;
-  if (!uninitialized_static) {
+  if (!uninitialized_static || VM_Version::supports_fast_class_init_checks()) {
+#if !defined(X86) && !defined(AARCH64)
+    guarantee(!uninitialized_static, "fast class init checks missing in interpreter"); // FIXME
+#endif // !X86 && !AARCH64
     get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield);
     if ((is_put && !has_initialized_final_update) || !info.access_flags().is_final()) {
       put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
     }
   }
@@ -722,11 +872,11 @@
 // The interpreter's synchronization code is factored out so that it can
 // be shared by method invocation and synchronized blocks.
 //%note synchronization_3
 
 //%note monitor_1
-JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* current, BasicObjectLock* elem))
+JRT_ENTRY_NO_ASYNC_PROF(void, InterpreterRuntime, monitorenter, InterpreterRuntime::monitorenter(JavaThread* current, BasicObjectLock* elem))
 #ifdef ASSERT
   current->last_frame().interpreter_frame_verify_monitor(elem);
 #endif
   Handle h_obj(current, elem->obj());
   assert(Universe::heap()->is_in_or_null(h_obj()),
@@ -737,11 +887,11 @@
 #ifdef ASSERT
   if (!current->preempting()) current->last_frame().interpreter_frame_verify_monitor(elem);
 #endif
 JRT_END
 
-JRT_LEAF(void, InterpreterRuntime::monitorexit(BasicObjectLock* elem))
+JRT_LEAF_PROF_NO_THREAD(void, InterpreterRuntime, monitorexit, InterpreterRuntime::monitorexit(BasicObjectLock* elem))
   oop obj = elem->obj();
   assert(Universe::heap()->is_in(obj), "must be an object");
   // The object could become unlocked through a JNI call, which we have no other checks for.
   // Give a fatal message if CheckJNICalls. Otherwise we ignore it.
   if (obj->is_unlocked()) {
@@ -755,16 +905,18 @@
   // again at method exit or in the case of an exception.
   elem->set_obj(nullptr);
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::throw_illegal_monitor_state_exception(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, throw_illegal_monitor_state_exception,
+           InterpreterRuntime::throw_illegal_monitor_state_exception(JavaThread* current))
   THROW(vmSymbols::java_lang_IllegalMonitorStateException());
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::new_illegal_monitor_state_exception(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, new_illegal_monitor_state_exception,
+           InterpreterRuntime::new_illegal_monitor_state_exception(JavaThread* current))
   // Returns an illegal exception to install into the current thread. The
   // pending_exception flag is cleared so normal exception handling does not
   // trigger. Any current installed exception will be overwritten. This
   // method will be called during an exception unwind.
 
@@ -778,22 +930,38 @@
 
 
 //------------------------------------------------------------------------------------------------------------------------
 // Invokes
 
-JRT_ENTRY(Bytecodes::Code, InterpreterRuntime::get_original_bytecode_at(JavaThread* current, Method* method, address bcp))
+JRT_ENTRY_PROF(Bytecodes::Code, InterpreterRuntime, get_original_bytecode_at, InterpreterRuntime::get_original_bytecode_at(JavaThread* current, Method* method, address bcp))
   return method->orig_bytecode_at(method->bci_from(bcp));
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::set_original_bytecode_at(JavaThread* current, Method* method, address bcp, Bytecodes::Code new_code))
+JRT_ENTRY_PROF(void, InterpreterRuntime, set_original_bytecode_at, InterpreterRuntime::set_original_bytecode_at(JavaThread* current, Method* method, address bcp, Bytecodes::Code new_code))
   method->set_orig_bytecode_at(method->bci_from(bcp), new_code);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::_breakpoint(JavaThread* current, Method* method, address bcp))
+JRT_ENTRY_PROF(void, InterpreterRuntime, breakpoint, InterpreterRuntime::_breakpoint(JavaThread* current, Method* method, address bcp))
   JvmtiExport::post_raw_breakpoint(current, method, bcp);
 JRT_END
 
+PROF_ENTRY(void, InterpreterRuntime, resolve_invokevirtual, InterpreterRuntime::resolve_invokevirtual(JavaThread* current))
+  resolve_invoke(current, Bytecodes::_invokevirtual);
+PROF_END
+
+PROF_ENTRY(void, InterpreterRuntime, resolve_invokespecial, InterpreterRuntime::resolve_invokespecial(JavaThread* current))
+  resolve_invoke(current, Bytecodes::_invokespecial);
+PROF_END
+
+PROF_ENTRY(void, InterpreterRuntime, resolve_invokestatic, InterpreterRuntime::resolve_invokestatic(JavaThread* current))
+  resolve_invoke(current, Bytecodes::_invokestatic);
+PROF_END
+
+PROF_ENTRY(void, InterpreterRuntime, resolve_invokeinterface, InterpreterRuntime::resolve_invokeinterface(JavaThread* current))
+  resolve_invoke(current, Bytecodes::_invokeinterface);
+PROF_END
+
 void InterpreterRuntime::resolve_invoke(JavaThread* current, Bytecodes::Code bytecode) {
   LastFrameAccessor last_frame(current);
   // extract receiver from the outgoing argument list if necessary
   Handle receiver(current, nullptr);
   if (bytecode == Bytecodes::_invokevirtual || bytecode == Bytecodes::_invokeinterface ||
@@ -908,10 +1076,11 @@
   if (!link_info.resolved_klass()->is_instance_klass() || InstanceKlass::cast(link_info.resolved_klass())->is_linked()) {
     CallInfo call_info;
     switch (bytecode) {
       case Bytecodes::_invokevirtual:   LinkResolver::cds_resolve_virtual_call  (call_info, link_info, CHECK); break;
       case Bytecodes::_invokeinterface: LinkResolver::cds_resolve_interface_call(call_info, link_info, CHECK); break;
+      case Bytecodes::_invokestatic:    LinkResolver::cds_resolve_static_call   (call_info, link_info, CHECK); break;
       case Bytecodes::_invokespecial:   LinkResolver::cds_resolve_special_call  (call_info, link_info, CHECK); break;
 
       default: fatal("Unimplemented: %s", Bytecodes::name(bytecode));
     }
     methodHandle resolved_method(THREAD, call_info.resolved_method());
@@ -928,11 +1097,11 @@
                            resolved_iklass->external_name());
   }
 }
 
 // First time execution:  Resolve symbols, create a permanent MethodType object.
-void InterpreterRuntime::resolve_invokehandle(JavaThread* current) {
+PROF_ENTRY(void, InterpreterRuntime, resolve_invokehandle, InterpreterRuntime::resolve_invokehandle(JavaThread* current))
   const Bytecodes::Code bytecode = Bytecodes::_invokehandle;
   LastFrameAccessor last_frame(current);
 
   // resolve method
   CallInfo info;
@@ -945,11 +1114,11 @@
                                  method_index, bytecode,
                                  CHECK);
   } // end JvmtiHideSingleStepping
 
   pool->cache()->set_method_handle(method_index, info);
-}
+PROF_END
 
 void InterpreterRuntime::cds_resolve_invokehandle(int raw_index,
                                                   constantPoolHandle& pool, TRAPS) {
   const Bytecodes::Code bytecode = Bytecodes::_invokehandle;
   CallInfo info;
@@ -957,11 +1126,11 @@
 
   pool->cache()->set_method_handle(raw_index, info);
 }
 
 // First time execution:  Resolve symbols, create a permanent CallSite object.
-void InterpreterRuntime::resolve_invokedynamic(JavaThread* current) {
+PROF_ENTRY(void, InterpreterRuntime, resolve_invokedynamic, InterpreterRuntime::resolve_invokedynamic(JavaThread* current))
   LastFrameAccessor last_frame(current);
   const Bytecodes::Code bytecode = Bytecodes::_invokedynamic;
 
   // resolve method
   CallInfo info;
@@ -973,11 +1142,11 @@
     LinkResolver::resolve_invoke(info, Handle(), pool,
                                  index, bytecode, CHECK);
   } // end JvmtiHideSingleStepping
 
   pool->cache()->set_dynamic_call(info, index);
-}
+PROF_END
 
 void InterpreterRuntime::cds_resolve_invokedynamic(int raw_index,
                                                    constantPoolHandle& pool, TRAPS) {
   const Bytecodes::Code bytecode = Bytecodes::_invokedynamic;
   CallInfo info;
@@ -987,29 +1156,25 @@
 
 // This function is the interface to the assembly code. It returns the resolved
 // cpCache entry.  This doesn't safepoint, but the helper routines safepoint.
 // This function will check for redefinition!
 JRT_ENTRY(void, InterpreterRuntime::resolve_from_cache(JavaThread* current, Bytecodes::Code bytecode)) {
+  trace_current_location(current);
+
   switch (bytecode) {
-  case Bytecodes::_getstatic:
-  case Bytecodes::_putstatic:
-  case Bytecodes::_getfield:
-  case Bytecodes::_putfield:
-    resolve_get_put(current, bytecode);
-    break;
-  case Bytecodes::_invokevirtual:
-  case Bytecodes::_invokespecial:
-  case Bytecodes::_invokestatic:
-  case Bytecodes::_invokeinterface:
-    resolve_invoke(current, bytecode);
-    break;
-  case Bytecodes::_invokehandle:
-    resolve_invokehandle(current);
-    break;
-  case Bytecodes::_invokedynamic:
-    resolve_invokedynamic(current);
-    break;
+  case Bytecodes::_getstatic: resolve_getstatic(current); break;
+  case Bytecodes::_putstatic: resolve_putstatic(current); break;
+  case Bytecodes::_getfield:  resolve_getfield(current);  break;
+  case Bytecodes::_putfield:  resolve_putfield(current);  break;
+
+  case Bytecodes::_invokevirtual:   resolve_invokevirtual(current);   break;
+  case Bytecodes::_invokespecial:   resolve_invokespecial(current);   break;
+  case Bytecodes::_invokestatic:    resolve_invokestatic(current);    break;
+  case Bytecodes::_invokeinterface: resolve_invokeinterface(current); break;
+  case Bytecodes::_invokehandle:    resolve_invokehandle(current);    break;
+  case Bytecodes::_invokedynamic:   resolve_invokedynamic(current);   break;
+
   default:
     fatal("unexpected bytecode: %s", Bytecodes::name(bytecode));
     break;
   }
 }
@@ -1061,11 +1226,11 @@
   }
 #endif
   return nm;
 }
 
-JRT_ENTRY(nmethod*,
+JRT_ENTRY_PROF(nmethod*, InterpreterRuntime, frequency_counter_overflow,
           InterpreterRuntime::frequency_counter_overflow_inner(JavaThread* current, address branch_bcp))
   // use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized
   // flag, in case this method triggers classloading which will call into Java.
   UnlockFlagSaver fs(current);
 
@@ -1084,11 +1249,11 @@
     }
   }
   return osr_nm;
 JRT_END
 
-JRT_LEAF(jint, InterpreterRuntime::bcp_to_di(Method* method, address cur_bcp))
+JRT_LEAF_PROF_NO_THREAD(jint, InterpreterRuntime, bcp_to_di, InterpreterRuntime::bcp_to_di(Method* method, address cur_bcp))
   assert(ProfileInterpreter, "must be profiling interpreter");
   int bci = method->bci_from(cur_bcp);
   MethodData* mdo = method->method_data();
   if (mdo == nullptr)  return 0;
   return mdo->bci_to_di(bci);
@@ -1121,11 +1286,11 @@
   }
   assert(mdp == mdp2, "wrong mdp");
 JRT_END
 #endif // ASSERT
 
-JRT_ENTRY(void, InterpreterRuntime::update_mdp_for_ret(JavaThread* current, int return_bci))
+JRT_ENTRY_PROF(void, InterpreterRuntime, update_mdp_for_ret, InterpreterRuntime::update_mdp_for_ret(JavaThread* current, int return_bci))
   assert(ProfileInterpreter, "must be profiling interpreter");
   ResourceMark rm(current);
   LastFrameAccessor last_frame(current);
   assert(last_frame.is_interpreted_frame(), "must come from interpreter");
   MethodData* h_mdo = last_frame.method()->method_data();
@@ -1141,16 +1306,16 @@
   RetData* rdata = data->as_RetData();
   address new_mdp = rdata->fixup_ret(return_bci, h_mdo);
   last_frame.set_mdp(new_mdp);
 JRT_END
 
-JRT_ENTRY(MethodCounters*, InterpreterRuntime::build_method_counters(JavaThread* current, Method* m))
+JRT_ENTRY_PROF(MethodCounters*, InterpreterRuntime, build_method_counters, InterpreterRuntime::build_method_counters(JavaThread* current, Method* m))
   return Method::build_method_counters(current, m);
 JRT_END
 
 
-JRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, at_safepoint, InterpreterRuntime::at_safepoint(JavaThread* current))
   // We used to need an explicit preserve_arguments here for invoke bytecodes. However,
   // stack traversal automatically takes care of preserving arguments for invoke, so
   // this is no longer needed.
 
   // JRT_END does an implicit safepoint check, hence we are guaranteed to block
@@ -1168,11 +1333,11 @@
     LastFrameAccessor last_frame(current);
     JvmtiExport::at_single_stepping_point(current, last_frame.method(), last_frame.bcp());
   }
 JRT_END
 
-JRT_LEAF(void, InterpreterRuntime::at_unwind(JavaThread* current))
+JRT_LEAF_PROF(void, InterpreterRuntime, at_unwind, InterpreterRuntime::at_unwind(JavaThread* current))
   assert(current == JavaThread::current(), "pre-condition");
   // This function is called by the interpreter when the return poll found a reason
   // to call the VM. The reason could be that we are returning into a not yet safe
   // to access frame. We handle that below.
   // Note that this path does not check for single stepping, because we do not want
@@ -1180,12 +1345,12 @@
   // such single stepping code will use the safepoint table, which will use the
   // InterpreterRuntime::at_safepoint callback.
   StackWatermarkSet::before_unwind(current);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread* current, oopDesc* obj,
-                                                      ResolvedFieldEntry *entry))
+JRT_ENTRY_PROF(void, InterpreterRuntime, post_field_access, InterpreterRuntime::post_field_access(JavaThread* current, oopDesc* obj,
+                                                                               ResolvedFieldEntry *entry))
 
   // check the access_flags for the field in the klass
 
   InstanceKlass* ik = entry->field_holder();
   int index = entry->field_index();
@@ -1203,12 +1368,12 @@
   jfieldID fid = jfieldIDWorkaround::to_jfieldID(field_holder, entry->field_offset(), is_static);
   LastFrameAccessor last_frame(current);
   JvmtiExport::post_field_access(current, last_frame.method(), last_frame.bcp(), field_holder, h_obj, fid);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::post_field_modification(JavaThread* current, oopDesc* obj,
-                                                            ResolvedFieldEntry *entry, jvalue *value))
+JRT_ENTRY_PROF(void, InterpreterRuntime, post_field_modification, InterpreterRuntime::post_field_modification(JavaThread* current, oopDesc* obj,
+                                                                                           ResolvedFieldEntry *entry, jvalue *value))
 
   InstanceKlass* ik = entry->field_holder();
 
   // check the access_flags for the field in the klass
   int index = entry->field_index();
@@ -1259,25 +1424,25 @@
   LastFrameAccessor last_frame(current);
   JvmtiExport::post_raw_field_modification(current, last_frame.method(), last_frame.bcp(), ik, h_obj,
                                            fid, sig_type, &fvalue);
 JRT_END
 
-JRT_ENTRY(void, InterpreterRuntime::post_method_entry(JavaThread* current))
+JRT_ENTRY_PROF(void, InterpreterRuntime, post_method_entry, InterpreterRuntime::post_method_entry(JavaThread* current))
   LastFrameAccessor last_frame(current);
   JvmtiExport::post_method_entry(current, last_frame.method(), last_frame.get_frame());
 JRT_END
 
 
 // This is a JRT_BLOCK_ENTRY because we have to stash away the return oop
 // before transitioning to VM, and restore it after transitioning back
 // to Java. The return oop at the top-of-stack, is not walked by the GC.
-JRT_BLOCK_ENTRY(void, InterpreterRuntime::post_method_exit(JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, InterpreterRuntime, post_method_exit, InterpreterRuntime::post_method_exit(JavaThread* current))
   LastFrameAccessor last_frame(current);
   JvmtiExport::post_method_exit(current, last_frame.method(), last_frame.get_frame());
 JRT_END
 
-JRT_LEAF(int, InterpreterRuntime::interpreter_contains(address pc))
+JRT_LEAF_PROF_NO_THREAD(int, InterpreterRuntime, interpreter_contains, InterpreterRuntime::interpreter_contains(address pc))
 {
   return (Interpreter::contains(Continuation::get_top_return_pc_post_barrier(JavaThread::current(), pc)) ? 1 : 0);
 }
 JRT_END
 
@@ -1471,11 +1636,11 @@
 GrowableArray<uint64_t>* SignatureHandlerLibrary::_fingerprints = nullptr;
 GrowableArray<address>*  SignatureHandlerLibrary::_handlers     = nullptr;
 address                  SignatureHandlerLibrary::_buffer       = nullptr;
 
 
-JRT_ENTRY(void, InterpreterRuntime::prepare_native_call(JavaThread* current, Method* method))
+JRT_ENTRY_PROF(void, InterpreterRuntime, prepare_native_call, InterpreterRuntime::prepare_native_call(JavaThread* current, Method* method))
   methodHandle m(current, method);
   assert(m->is_native(), "sanity check");
   // lookup native function entry point if it doesn't exist
   if (!m->has_native_function()) {
     NativeLookup::lookup(m, CHECK);
@@ -1512,12 +1677,13 @@
 // Make sure it is an invokestatic of a polymorphic intrinsic that has a member_name argument
 // and return it as a vm_result so that it can be reloaded in the list of invokestatic parameters.
 // The member_name argument is a saved reference (in local#0) to the member_name.
 // For backward compatibility with some JDK versions (7, 8) it can also be a direct method handle.
 // FIXME: remove DMH case after j.l.i.InvokerBytecodeGenerator code shape is updated.
-JRT_ENTRY(void, InterpreterRuntime::member_name_arg_or_null(JavaThread* current, address member_name,
-                                                            Method* method, address bcp))
+JRT_ENTRY_PROF(void, InterpreterRuntime, member_name_arg_or_null,
+           InterpreterRuntime::member_name_arg_or_null(JavaThread* current, address member_name,
+                                                       Method* method, address bcp))
   Bytecodes::Code code = Bytecodes::code_at(method, bcp);
   if (code != Bytecodes::_invokestatic) {
     return;
   }
   ConstantPool* cpool = method->constants();
@@ -1550,5 +1716,107 @@
   methodHandle mh(current, last_frame.method());
   BytecodeTracer::trace_interpreter(mh, last_frame.bcp(), tos, tos2);
   return preserve_this_value;
 JRT_END
 #endif // !PRODUCT
+
+#define DO_COUNTERS(macro) \
+  macro(InterpreterRuntime, ldc) \
+  macro(InterpreterRuntime, resolve_ldc) \
+  macro(InterpreterRuntime, new) \
+  macro(InterpreterRuntime, newarray) \
+  macro(InterpreterRuntime, anewarray) \
+  macro(InterpreterRuntime, multianewarray) \
+  macro(InterpreterRuntime, register_finalizer) \
+  macro(InterpreterRuntime, quicken_io_cc) \
+  macro(InterpreterRuntime, throw_StackOverflowError) \
+  macro(InterpreterRuntime, throw_delayed_StackOverflowError) \
+  macro(InterpreterRuntime, create_exception) \
+  macro(InterpreterRuntime, create_klass_exception) \
+  macro(InterpreterRuntime, throw_ArrayIndexOutOfBoundsException) \
+  macro(InterpreterRuntime, throw_ClassCastException) \
+  macro(InterpreterRuntime, exception_handler_for_exception) \
+  macro(InterpreterRuntime, throw_pending_exception) \
+  macro(InterpreterRuntime, throw_AbstractMethodError) \
+  macro(InterpreterRuntime, throw_AbstractMethodErrorWithMethod) \
+  macro(InterpreterRuntime, throw_AbstractMethodErrorVerbose) \
+  macro(InterpreterRuntime, throw_IncompatibleClassChangeError) \
+  macro(InterpreterRuntime, throw_IncompatibleClassChangeErrorVerbose) \
+  macro(InterpreterRuntime, throw_NullPointerException) \
+  macro(InterpreterRuntime, monitorenter) \
+  macro(InterpreterRuntime, monitorexit) \
+  macro(InterpreterRuntime, throw_illegal_monitor_state_exception) \
+  macro(InterpreterRuntime, new_illegal_monitor_state_exception) \
+  macro(InterpreterRuntime, get_original_bytecode_at) \
+  macro(InterpreterRuntime, set_original_bytecode_at) \
+  macro(InterpreterRuntime, breakpoint) \
+  macro(InterpreterRuntime, resolve_getfield) \
+  macro(InterpreterRuntime, resolve_putfield) \
+  macro(InterpreterRuntime, resolve_getstatic) \
+  macro(InterpreterRuntime, resolve_putstatic) \
+  macro(InterpreterRuntime, resolve_invokevirtual) \
+  macro(InterpreterRuntime, resolve_invokespecial) \
+  macro(InterpreterRuntime, resolve_invokestatic) \
+  macro(InterpreterRuntime, resolve_invokeinterface) \
+  macro(InterpreterRuntime, resolve_invokehandle) \
+  macro(InterpreterRuntime, resolve_invokedynamic) \
+  macro(InterpreterRuntime, frequency_counter_overflow) \
+  macro(InterpreterRuntime, bcp_to_di) \
+  macro(InterpreterRuntime, update_mdp_for_ret) \
+  macro(InterpreterRuntime, build_method_counters) \
+  macro(InterpreterRuntime, at_safepoint) \
+  macro(InterpreterRuntime, at_unwind) \
+  macro(InterpreterRuntime, post_field_access) \
+  macro(InterpreterRuntime, post_field_modification) \
+  macro(InterpreterRuntime, post_method_entry) \
+  macro(InterpreterRuntime, post_method_exit) \
+  macro(InterpreterRuntime, interpreter_contains) \
+  macro(InterpreterRuntime, prepare_native_call)
+
+#if INCLUDE_JVMTI
+#define DO_JVMTI_COUNTERS(macro) \
+  macro(InterpreterRuntime, member_name_arg_or_null)
+#else
+#define DO_JVMTI_COUNTERS(macro)
+#endif /* INCLUDE_JVMTI */
+
+#define INIT_COUNTER(sub, name) \
+  NEWPERFTICKCOUNTERS(_perf_##sub##_##name##_timer, SUN_CI, #sub "::" #name); \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_CI, #sub "::" #name "_count");
+
+void InterpreterRuntime::init_counters() {
+  if (UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS(INIT_COUNTER)
+    DO_JVMTI_COUNTERS(INIT_COUNTER)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("jvm_perf_init failed unexpectedly");
+    }
+  }
+}
+#undef INIT_COUNTER
+
+#define PRINT_COUNTER(sub, name) { \
+  jlong count = _perf_##sub##_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-50s = " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) (" JLONG_FORMAT_W(5) " events)", \
+                 #sub "::" #name, \
+                 _perf_##sub##_##name##_timer->elapsed_counter_value_us(), \
+                 _perf_##sub##_##name##_timer->thread_counter_value_us(), \
+                 count); \
+  }}
+
+void InterpreterRuntime::print_counters_on(outputStream* st) {
+  if (UsePerfData && ProfileRuntimeCalls) {
+    DO_COUNTERS(PRINT_COUNTER)
+    DO_JVMTI_COUNTERS(PRINT_COUNTER)
+  } else {
+    st->print_cr("  InterpreterRuntime: no info (%s is disabled)", (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData"));
+  }
+}
+
+#undef PRINT_COUNTER
+#undef DO_JVMTI_COUNTERS
+#undef DO_COUNTERS
+
diff a/src/hotspot/share/interpreter/interpreterRuntime.hpp b/src/hotspot/share/interpreter/interpreterRuntime.hpp
--- a/src/hotspot/share/interpreter/interpreterRuntime.hpp
+++ b/src/hotspot/share/interpreter/interpreterRuntime.hpp
@@ -101,13 +101,23 @@
                                        constantPoolHandle& pool, TRAPS);
   static void cds_resolve_invokedynamic(int raw_index,
                                         constantPoolHandle& pool, TRAPS);
 private:
   // Statics & fields
+  static void resolve_getfield(JavaThread* current);
+  static void resolve_putfield(JavaThread* current);
+  static void resolve_getstatic(JavaThread* current);
+  static void resolve_putstatic(JavaThread* current);
+
   static void resolve_get_put(JavaThread* current, Bytecodes::Code bytecode);
 
   // Calls
+  static void resolve_invokevirtual(JavaThread* current);
+  static void resolve_invokespecial(JavaThread* current);
+  static void resolve_invokestatic(JavaThread* current);
+  static void resolve_invokeinterface(JavaThread* current);
+
   static void resolve_invoke(JavaThread* current, Bytecodes::Code bytecode);
   static void resolve_invokehandle (JavaThread* current);
   static void resolve_invokedynamic(JavaThread* current);
 
   static void update_invoke_cp_cache_entry(CallInfo& info, Bytecodes::Code bytecode,
@@ -168,10 +178,13 @@
   static void    update_mdp_for_ret(JavaThread* current, int bci);
 #ifdef ASSERT
   static void    verify_mdp(Method* method, address bcp, address mdp);
 #endif // ASSERT
   static MethodCounters* build_method_counters(JavaThread* current, Method* m);
+
+  static void init_counters();
+  static void print_counters_on(outputStream* st);
 };
 
 
 class SignatureHandlerLibrary: public AllStatic {
  public:
diff a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp
--- a/src/hotspot/share/interpreter/linkResolver.cpp
+++ b/src/hotspot/share/interpreter/linkResolver.cpp
@@ -988,11 +988,12 @@
   resolve_field(fd, link_info, byte, initialize_class, CHECK);
 }
 
 void LinkResolver::resolve_field(fieldDescriptor& fd,
                                  const LinkInfo& link_info,
-                                 Bytecodes::Code byte, bool initialize_class,
+                                 Bytecodes::Code byte,
+                                 bool initialize_class,
                                  TRAPS) {
   assert(byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic ||
          byte == Bytecodes::_getfield  || byte == Bytecodes::_putfield  ||
          byte == Bytecodes::_nofast_getfield  || byte == Bytecodes::_nofast_putfield  ||
          (byte == Bytecodes::_nop && !link_info.check_access()), "bad field access bytecode");
@@ -1121,10 +1122,14 @@
   // setup result
   result.set_static(resolved_klass, methodHandle(THREAD, resolved_method), CHECK);
   JFR_ONLY(Jfr::on_resolution(result, CHECK);)
 }
 
+void LinkResolver::cds_resolve_static_call(CallInfo& result, const LinkInfo& link_info, TRAPS) {
+  resolve_static_call(result, link_info, /*initialize_class*/false, CHECK);
+}
+
 // throws linktime exceptions
 Method* LinkResolver::linktime_resolve_static_method(const LinkInfo& link_info, TRAPS) {
 
   Klass* resolved_klass = link_info.resolved_klass();
   Method* resolved_method;
@@ -1777,24 +1782,24 @@
     return false;
   }
 }
 
 void LinkResolver::resolve_invokehandle(CallInfo& result, const constantPoolHandle& pool, int index, TRAPS) {
-
   PerfTraceTimedEvent timer(ClassLoader::perf_resolve_invokehandle_time(),
-                            ClassLoader::perf_resolve_invokehandle_count());
+                            ClassLoader::perf_resolve_invokehandle_count(),
+                            THREAD->class_being_initialized() == nullptr);
 
   LinkInfo link_info(pool, index, Bytecodes::_invokehandle, CHECK);
+  { // Check if the call site has been bound already, and short circuit:
+    bool is_done = resolve_previously_linked_invokehandle(result, link_info, pool, index, CHECK);
+    if (is_done) return;
+  }
   if (log_is_enabled(Info, methodhandles)) {
     ResourceMark rm(THREAD);
     log_info(methodhandles)("resolve_invokehandle %s %s", link_info.name()->as_C_string(),
                             link_info.signature()->as_C_string());
   }
-  { // Check if the call site has been bound already, and short circuit:
-    bool is_done = resolve_previously_linked_invokehandle(result, link_info, pool, index, CHECK);
-    if (is_done) return;
-  }
   resolve_handle_call(result, link_info, CHECK);
 }
 
 void LinkResolver::resolve_handle_call(CallInfo& result,
                                        const LinkInfo& link_info,
@@ -1833,12 +1838,12 @@
   JFR_ONLY(Jfr::on_resolution(result, CHECK);)
 }
 
 void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int indy_index, TRAPS) {
   PerfTraceTimedEvent timer(ClassLoader::perf_resolve_invokedynamic_time(),
-                            ClassLoader::perf_resolve_invokedynamic_count());
-
+                            ClassLoader::perf_resolve_invokedynamic_count(),
+                            THREAD->class_being_initialized() == nullptr);
   int pool_index = pool->resolved_indy_entry_at(indy_index)->constant_pool_index();
 
   // Resolve the bootstrap specifier (BSM + optional arguments).
   BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index);
 
diff a/src/hotspot/share/interpreter/linkResolver.hpp b/src/hotspot/share/interpreter/linkResolver.hpp
--- a/src/hotspot/share/interpreter/linkResolver.hpp
+++ b/src/hotspot/share/interpreter/linkResolver.hpp
@@ -294,18 +294,10 @@
                                    const constantPoolHandle& pool,
                                    int index,
                                    const methodHandle& method,
                                    Bytecodes::Code byte,
                                    bool initialize_class, TRAPS);
-  static void resolve_field_access(fieldDescriptor& result,
-                                   const constantPoolHandle& pool,
-                                   int index,
-                                   const methodHandle& method,
-                                   Bytecodes::Code byte, TRAPS) {
-    resolve_field_access(result, pool, index, method, byte,
-                         /* initialize_class*/true, THREAD);
-  }
   static void resolve_field(fieldDescriptor& result, const LinkInfo& link_info,
                             Bytecodes::Code access_kind,
                             bool initialize_class, TRAPS);
 
   static void resolve_static_call   (CallInfo& result,
@@ -326,10 +318,11 @@
   static void resolve_dynamic_call  (CallInfo& result,
                                      BootstrapInfo& bootstrap_specifier, TRAPS);
 
   static void cds_resolve_virtual_call  (CallInfo& result, const LinkInfo& link_info, TRAPS);
   static void cds_resolve_interface_call(CallInfo& result, const LinkInfo& link_info, TRAPS);
+  static void cds_resolve_static_call   (CallInfo& result, const LinkInfo& link_info, TRAPS);
   static void cds_resolve_special_call  (CallInfo& result, const LinkInfo& link_info, TRAPS);
 
   // same as above for compile-time resolution; but returns null handle instead of throwing
   // an exception on error also, does not initialize klass (i.e., no side effects)
   static Method* resolve_virtual_call_or_null(Klass* receiver_klass,
diff a/src/hotspot/share/interpreter/rewriter.cpp b/src/hotspot/share/interpreter/rewriter.cpp
--- a/src/hotspot/share/interpreter/rewriter.cpp
+++ b/src/hotspot/share/interpreter/rewriter.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/cds_globals.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "interpreter/bytecodes.hpp"
 #include "interpreter/bytecodeStream.hpp"
diff a/src/hotspot/share/interpreter/templateInterpreter.cpp b/src/hotspot/share/interpreter/templateInterpreter.cpp
--- a/src/hotspot/share/interpreter/templateInterpreter.cpp
+++ b/src/hotspot/share/interpreter/templateInterpreter.cpp
@@ -55,10 +55,12 @@
   _code = new StubQueue(new InterpreterCodeletInterface, code_size + max_aligned_bytes, nullptr,
                         "Interpreter");
 }
 
 void TemplateInterpreter::initialize_code() {
+  InterpreterRuntime::init_counters();
+
   AbstractInterpreter::initialize();
 
   TemplateTable::initialize();
 
   // generate interpreter
diff a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
--- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
@@ -185,11 +185,14 @@
   }
 
   // all non-native method kinds
   method_entry(zerolocals)
   method_entry(zerolocals_synchronized)
+  method_entry(zerolocals_upcalls)
+  method_entry(zerolocals_synchronized_upcalls)
   method_entry(empty)
+  method_entry(empty_upcalls)
   method_entry(getter)
   method_entry(setter)
   method_entry(abstract)
   method_entry(java_lang_math_sin  )
   method_entry(java_lang_math_cos  )
@@ -221,10 +224,12 @@
     Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind, true); \
   }
 
   native_method_entry(native)
   native_method_entry(native_synchronized)
+  native_method_entry(native_upcalls)
+  native_method_entry(native_synchronized_upcalls)
 
   // Entries to intrinsics for native methods should follow
   // entries for `native` methods to use the same address in case
   // intrinsic is disabled.
   native_method_entry(java_lang_Thread_currentThread)
@@ -369,14 +374,16 @@
 
 
 //------------------------------------------------------------------------------------------------------------------------
 
 void TemplateInterpreterGenerator::generate_and_dispatch(Template* t, TosState tos_out) {
+  if (CountBytecodes || CountBytecodesPerThread || TraceBytecodes || StopInterpreterAt > 0) {
+    count_bytecode();
+  }
+  if (PrintBytecodeHistogram)                                    histogram_bytecode(t);
 #ifndef PRODUCT
   // debugging code
-  if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) count_bytecode();
-  if (PrintBytecodeHistogram)                                    histogram_bytecode(t);
   if (PrintBytecodePairHistogram)                                histogram_bytecode_pair(t);
   if (TraceBytecodes)                                            trace_bytecode(t);
   if (StopInterpreterAt > 0)                                     stop_interpreter_at();
   __ verify_FPU(1, t->tos_in());
 #endif // !PRODUCT
@@ -405,46 +412,101 @@
     // dispatch to next bytecode
     __ dispatch_epilog(tos_out, step);
   }
 }
 
+bool TemplateInterpreterGenerator::is_synchronized_method(AbstractInterpreter::MethodKind kind) {
+  switch (kind) {
+    case Interpreter::zerolocals_synchronized        : // fall thru
+    case Interpreter::zerolocals_synchronized_upcalls: // fall thru
+    case Interpreter::native_synchronized            : // fall thru
+    case Interpreter::native_synchronized_upcalls    :
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool TemplateInterpreterGenerator::is_runtime_upcalls_method(AbstractInterpreter::MethodKind kind) {
+  switch (kind) {
+    case Interpreter::zerolocals_upcalls             : // fall thru
+    case Interpreter::zerolocals_synchronized_upcalls: // fall thru
+    case Interpreter::native_upcalls                 : // fall thru
+    case Interpreter::native_synchronized_upcalls    : // fall thru
+    case Interpreter::empty_upcalls                  :
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool TemplateInterpreterGenerator::is_intrinsic_method(AbstractInterpreter::MethodKind kind) {
+  switch (kind) {
+    case Interpreter::zerolocals                     : // fall thru
+    case Interpreter::zerolocals_synchronized        : // fall thru
+    case Interpreter::zerolocals_upcalls             : // fall thru
+    case Interpreter::zerolocals_synchronized_upcalls: // fall thru
+    case Interpreter::native                         : // fall thru
+    case Interpreter::native_synchronized            : // fall thru
+    case Interpreter::native_upcalls                 : // fall thru
+    case Interpreter::native_synchronized_upcalls    : // fall thru
+    case Interpreter::empty                          : // fall thru
+    case Interpreter::empty_upcalls                  : // fall thru
+    case Interpreter::getter                         : // fall thru
+    case Interpreter::setter                         : // fall thru
+    case Interpreter::abstract                       :
+      return false;
+    default:
+      return true;
+  }
+}
+
+bool TemplateInterpreterGenerator::is_abstract_method(AbstractInterpreter::MethodKind kind) {
+  switch (kind) {
+    case Interpreter::abstract:
+      return true;
+    default:
+      return false;
+  }
+}
+
 // Generate method entries
 address TemplateInterpreterGenerator::generate_method_entry(
                                         AbstractInterpreter::MethodKind kind, bool native) {
-  // determine code generation flags
-  bool synchronized = false;
   address entry_point = nullptr;
 
-  switch (kind) {
-  case Interpreter::zerolocals             :                           break;
-  case Interpreter::zerolocals_synchronized: synchronized = true;      break;
-  case Interpreter::native                 :                           break;
-  case Interpreter::native_synchronized    : synchronized = true;      break;
-  case Interpreter::empty                  : break;
-  case Interpreter::getter                 : break;
-  case Interpreter::setter                 : break;
-  case Interpreter::abstract               : entry_point = generate_abstract_entry(); break;
-  default:
-    entry_point = generate_intrinsic_entry(kind); // process the rest
-    break;
+  if (is_abstract_method(kind)) {
+    entry_point = generate_abstract_entry();
+  } else if (is_intrinsic_method(kind)) {
+    entry_point = generate_intrinsic_entry(kind);
   }
 
   if (entry_point) {
     return entry_point;
   }
 
+  bool synchronized = is_synchronized_method(kind);
+  bool upcalls = is_runtime_upcalls_method(kind);
+
   // We expect the normal and native entry points to be generated first so we can reuse them.
-  if (native) {
-    entry_point = Interpreter::entry_for_kind(synchronized ? Interpreter::native_synchronized : Interpreter::native);
-    if (entry_point == nullptr) {
-      entry_point = generate_native_entry(synchronized);
-    }
-  } else {
-    entry_point = Interpreter::entry_for_kind(synchronized ? Interpreter::zerolocals_synchronized : Interpreter::zerolocals);
-    if (entry_point == nullptr) {
-      entry_point = generate_normal_entry(synchronized);
-    }
+  if (!synchronized && !upcalls) {
+    entry_point = Interpreter::entry_for_kind(native ? Interpreter::native
+                                                     : Interpreter::zerolocals);
+  } else if (synchronized && !upcalls) {
+    entry_point = Interpreter::entry_for_kind(native ? Interpreter::native_synchronized
+                                                     : Interpreter::zerolocals_synchronized);
+  } else if (!synchronized && upcalls) {
+    entry_point = Interpreter::entry_for_kind(native ? Interpreter::native_upcalls
+                                                     : Interpreter::zerolocals_upcalls);
+  } else if (synchronized && upcalls) {
+    entry_point = Interpreter::entry_for_kind(native ? Interpreter::native_synchronized_upcalls
+                                                     : Interpreter::zerolocals_synchronized_upcalls);
+  }
+
+  if (entry_point == nullptr) {
+      entry_point = native ? generate_native_entry(synchronized, upcalls)
+                           : generate_normal_entry(synchronized, upcalls);
   }
 
   return entry_point;
 }
 
diff a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp
--- a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp
+++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp
@@ -74,26 +74,32 @@
   void set_entry_points_for_all_bytes();
   void set_safepoints_for_all_bytes();
 
   // Helpers for generate_and_dispatch
   address generate_trace_code(TosState state)   PRODUCT_RETURN_NULL;
-  void count_bytecode()                         PRODUCT_RETURN;
-  void histogram_bytecode(Template* t)          PRODUCT_RETURN;
+  void count_bytecode();
+  void histogram_bytecode(Template* t);
   void histogram_bytecode_pair(Template* t)     PRODUCT_RETURN;
   void trace_bytecode(Template* t)              PRODUCT_RETURN;
   void stop_interpreter_at()                    PRODUCT_RETURN;
 
   void generate_all();
 
+  // helpers for method entry generation
+  bool is_synchronized_method(AbstractInterpreter::MethodKind kind);
+  bool is_runtime_upcalls_method(AbstractInterpreter::MethodKind kind);
+  bool is_intrinsic_method(AbstractInterpreter::MethodKind kind);
+  bool is_abstract_method(AbstractInterpreter::MethodKind kind);
+
   // entry point generator
   address generate_method_entry(AbstractInterpreter::MethodKind kind, bool native);
 
   // generate intrinsic method entries
   address generate_intrinsic_entry(AbstractInterpreter::MethodKind kind);
 
-  address generate_normal_entry(bool synchronized);
-  address generate_native_entry(bool synchronized);
+  address generate_normal_entry(bool synchronized, bool runtime_upcalls);
+  address generate_native_entry(bool synchronized, bool runtime_upcalls);
   address generate_abstract_entry(void);
   address generate_math_entry(AbstractInterpreter::MethodKind kind);
   address generate_Reference_get_entry();
   address generate_CRC32_update_entry();
   address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind);
diff a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
--- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
@@ -119,16 +119,16 @@
 
   // We expect the normal and native entry points to be generated first so we can reuse them.
   if (native) {
     entry_point = Interpreter::entry_for_kind(synchronized ? Interpreter::native_synchronized : Interpreter::native);
     if (entry_point == nullptr) {
-      entry_point = generate_native_entry(synchronized);
+      entry_point = generate_native_entry(synchronized, false);
     }
   } else {
     entry_point = Interpreter::entry_for_kind(synchronized ? Interpreter::zerolocals_synchronized : Interpreter::zerolocals);
     if (entry_point == nullptr) {
-      entry_point = generate_normal_entry(synchronized);
+      entry_point = generate_normal_entry(synchronized, false);
     }
   }
 
   return entry_point;
 }
@@ -174,12 +174,12 @@
 
 address ZeroInterpreterGenerator::generate_Reference_get_entry(void) {
   return generate_entry((address) ZeroInterpreter::Reference_get_entry);
 }
 
-address ZeroInterpreterGenerator::generate_native_entry(bool synchronized) {
+address ZeroInterpreterGenerator::generate_native_entry(bool synchronized, bool runtime_upcalls) {
   return generate_entry((address) ZeroInterpreter::native_entry);
 }
 
-address ZeroInterpreterGenerator::generate_normal_entry(bool synchronized) {
+address ZeroInterpreterGenerator::generate_normal_entry(bool synchronized, bool runtime_upcalls) {
   return generate_entry((address) ZeroInterpreter::normal_entry);
 }
diff a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp
--- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp
+++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp
@@ -37,12 +37,12 @@
   void generate_all();
 
   address generate_slow_signature_handler();
 
   address generate_method_entry(AbstractInterpreter::MethodKind kind);
-  address generate_normal_entry(bool synchronized);
-  address generate_native_entry(bool synchronized);
+  address generate_normal_entry(bool synchronized, bool runtime_upcalls);
+  address generate_native_entry(bool synchronized, bool runtime_upcalls);
   address generate_abstract_entry();
   address generate_math_entry(AbstractInterpreter::MethodKind kind);
   address generate_empty_entry();
   address generate_getter_entry();
   address generate_setter_entry();
diff a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
--- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
+++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
@@ -1470,10 +1470,11 @@
 
 static ClassFileStream* schema_extend_event_subklass_bytes(const InstanceKlass* ik, const ClassFileParser& parser, bool& is_instrumented, TRAPS) {
   assert(JdkJfrEvent::is_a(ik), "invariant");
   assert(!is_instrumented, "invariant");
   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
+  initialize_symbols();
   if (invalid_preconditions_for_subklass_on_initial_load(ik)) {
     // Remove the tag denoting this as a jdk.jfr.Event subklass. No instrumentation, hence no events can be written.
     // The class is allowed to load as-is, but it is classified as outside of the jfr system.
     JdkJfrEvent::remove(ik);
     return nullptr;
diff a/src/hotspot/share/jvmci/jvmciCompiler.cpp b/src/hotspot/share/jvmci/jvmciCompiler.cpp
--- a/src/hotspot/share/jvmci/jvmciCompiler.cpp
+++ b/src/hotspot/share/jvmci/jvmciCompiler.cpp
@@ -88,11 +88,11 @@
   for (int i = 0; i < len; i++) {
     methodHandle mh(THREAD, objectMethods->at(i));
     if (!mh->is_native() && !mh->is_static() && !mh->is_object_initializer() && !mh->is_static_initializer()) {
       ResourceMark rm;
       int hot_count = 10; // TODO: what's the appropriate value?
-      CompileBroker::compile_method(mh, InvocationEntryBci, CompLevel_full_optimization, mh, hot_count, CompileTask::Reason_Bootstrap, CHECK);
+      CompileBroker::compile_method(mh, InvocationEntryBci, CompLevel_full_optimization, mh, hot_count, false, CompileTask::Reason_Bootstrap, CHECK);
     }
   }
 
   int qsize;
   bool first_round = true;
diff a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp
@@ -2165,11 +2165,11 @@
                                  offsets,
                                  orig_pc_offset,
                                  debug_info, dependencies, code_buffer,
                                  frame_words, oop_map_set,
                                  handler_table, implicit_exception_table,
-                                 compiler, comp_level,
+                                 compiler, comp_level, nullptr /* SCCEntry */,
                                  speculations, speculations_len, data);
 
 
       // Free codeBlobs
       if (nm == nullptr) {
diff a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp
--- a/src/hotspot/share/logging/logTag.hpp
+++ b/src/hotspot/share/logging/logTag.hpp
@@ -108,20 +108,22 @@
   LOG_TAG(jmethod) \
   LOG_TAG(jni) \
   LOG_TAG(jvmci) \
   LOG_TAG(jvmti) \
   LOG_TAG(lambda) \
+  LOG_TAG(leyden) \
   LOG_TAG(library) \
   LOG_TAG(link) \
   LOG_TAG(liveness) \
   LOG_TAG(load) /* Trace all classes loaded */ \
   LOG_TAG(loader) \
   LOG_TAG(logging) \
   LOG_TAG(malloc) \
   LOG_TAG(map) \
   LOG_TAG(mark) \
-  LOG_TAG(marking) \
+  LOG_TAG(marking)   \
+  LOG_TAG(mdo) \
   LOG_TAG(membername) \
   LOG_TAG(memops) \
   LOG_TAG(metadata) \
   LOG_TAG(metaspace) \
   LOG_TAG(methodcomparator) \
@@ -155,15 +157,18 @@
   LOG_TAG(perf) \
   LOG_TAG(periodic) \
   LOG_TAG(phases) \
   LOG_TAG(plab) \
   LOG_TAG(placeholders) \
+  LOG_TAG(precompile) \
   LOG_TAG(preempt) \
   LOG_TAG(preorder)  /* Trace all classes loaded in order referenced (not loaded) */ \
   LOG_TAG(preview)   /* Trace loading of preview feature types */ \
+  LOG_TAG(profiling) \
   LOG_TAG(promotion) \
   LOG_TAG(protectiondomain) /* "Trace protection domain verification" */ \
+  LOG_TAG(proxy) \
   LOG_TAG(ptrqueue) \
   LOG_TAG(purge) \
   LOG_TAG(record) \
   LOG_TAG(redefine) \
   LOG_TAG(ref) \
@@ -173,10 +178,11 @@
   LOG_TAG(remset) \
   LOG_TAG(resolve) \
   LOG_TAG(safepoint) \
   LOG_TAG(sampling) \
   LOG_TAG(scavenge) \
+  LOG_TAG(scc) \
   LOG_TAG(sealed) \
   LOG_TAG(setting) \
   LOG_TAG(smr) \
   LOG_TAG(stackbarrier) \
   LOG_TAG(stackmap) \
@@ -203,10 +209,11 @@
   LOG_TAG(thread) \
   LOG_TAG(throttle) \
   LOG_TAG(timer) \
   LOG_TAG(tlab) \
   LOG_TAG(tracking) \
+  LOG_TAG(training) \
   LOG_TAG(trimnative) /* trim native heap */ \
   LOG_TAG(unload) /* Trace unloading of classes */ \
   LOG_TAG(unmap) \
   LOG_TAG(unshareable) \
   NOT_PRODUCT(LOG_TAG(upcall)) \
diff a/src/hotspot/share/memory/allocation.cpp b/src/hotspot/share/memory/allocation.cpp
--- a/src/hotspot/share/memory/allocation.cpp
+++ b/src/hotspot/share/memory/allocation.cpp
@@ -85,10 +85,18 @@
   assert(!Thread::current()->is_Java_thread(), "only allowed by non-Java thread");
   assert(type != ClassType, "class has its own operator new");
   return Metaspace::allocate(loader_data, word_size, type, /*use_class_space*/ false);
 }
 
+
+// Work-around -- see JDK-8331086
+void* MetaspaceObj::operator new(size_t size, MemTag flags) throw() {
+  void* p = AllocateHeap(size, flags, CALLER_PC);
+  memset(p, 0, size);
+  return p;
+}
+
 bool MetaspaceObj::is_valid(const MetaspaceObj* p) {
   // Weed out obvious bogus values first without traversing metaspace
   if ((size_t)p < os::min_page_size()) {
     return false;
   } else if (!is_aligned((address)p, sizeof(MetaWord))) {
diff a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp
--- a/src/hotspot/share/memory/allocation.hpp
+++ b/src/hotspot/share/memory/allocation.hpp
@@ -312,12 +312,15 @@
   f(MethodData) \
   f(ConstantPool) \
   f(ConstantPoolCache) \
   f(Annotations) \
   f(MethodCounters) \
-  f(SharedClassPathEntry) \
-  f(RecordComponent)
+  f(RecordComponent) \
+  f(KlassTrainingData) \
+  f(MethodTrainingData) \
+  f(CompileTrainingData) \
+  f(SharedClassPathEntry)
 
 #define METASPACE_OBJ_TYPE_DECLARE(name) name ## Type,
 #define METASPACE_OBJ_TYPE_NAME_CASE(name) case name ## Type: return #name;
 
   enum Type {
@@ -333,10 +336,16 @@
       ShouldNotReachHere();
       return nullptr;
     }
   }
 
+  static bool is_training_data(Type type) {
+    return (type == Type::KlassTrainingDataType)  ||
+           (type == Type::MethodTrainingDataType) ||
+           (type == Type::CompileTrainingDataType);
+  }
+
   static MetaspaceObj::Type array_type(size_t elem_size) {
     switch (elem_size) {
     case 1: return TypeArrayU1Type;
     case 2: return TypeArrayU2Type;
     case 4: return TypeArrayU4Type;
@@ -351,10 +360,13 @@
                      Type type, JavaThread* thread) throw();
                      // can't use TRAPS from this header file.
   void* operator new(size_t size, ClassLoaderData* loader_data,
                      size_t word_size,
                      Type type) throw();
+
+  // HACK -- this is used for allocating training data. See JDK-8331086
+  void* operator new(size_t size, MemTag flags) throw();
   void operator delete(void* p) { ShouldNotCallThis(); }
 
   // Declare a *static* method with the same signature in any subclass of MetaspaceObj
   // that should be read-only by default. See symbol.hpp for an example. This function
   // is used by the templates in metaspaceClosure.hpp
diff a/src/hotspot/share/memory/metadataFactory.hpp b/src/hotspot/share/memory/metadataFactory.hpp
--- a/src/hotspot/share/memory/metadataFactory.hpp
+++ b/src/hotspot/share/memory/metadataFactory.hpp
@@ -46,10 +46,17 @@
       array->at_put(i, value);
     }
     return array;
   }
 
+  // Work-around -- see JDK-8331086.
+  // This API should be used for TrainingData only.
+  template <typename T>
+  static Array<T>* new_array_from_c_heap(int length, MemTag flags) {
+    return new (length, flags) Array<T>(length);
+  }
+
   template <typename T>
   static void free_array(ClassLoaderData* loader_data, Array<T>* data) {
     if (data != nullptr) {
       assert(loader_data != nullptr, "shouldn't pass null");
       assert(!data->is_shared(), "cannot deallocate array in shared spaces");
diff a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp
--- a/src/hotspot/share/memory/metaspace.cpp
+++ b/src/hotspot/share/memory/metaspace.cpp
@@ -711,11 +711,13 @@
   MetaspaceGC::initialize(); // <- since we do not prealloc init chunks anymore is this still needed?
 
   metaspace::ChunkHeaderPool::initialize();
 
   if (CDSConfig::is_dumping_static_archive()) {
-    assert(!CDSConfig::is_using_archive(), "sanity");
+    if (!CDSConfig::is_dumping_final_static_archive()) {
+      assert(!CDSConfig::is_using_archive(), "sanity");
+    }
     MetaspaceShared::initialize_for_static_dump();
   }
 
   // If UseCompressedClassPointers=1, we have two cases:
   // a) if CDS is active (runtime, Xshare=on), it will create the class space
@@ -887,10 +889,11 @@
 
   if (HAS_PENDING_EXCEPTION) {
     assert(false, "Should not allocate with exception pending");
     return nullptr;  // caller does a CHECK_NULL too
   }
+  //leyden/premain: temporarily disabled due to JDK-8327737
   assert(!THREAD->owns_locks(), "allocating metaspace while holding mutex");
 
   MetaWord* result = allocate(loader_data, word_size, type, use_class_space);
 
   if (result == nullptr) {
diff a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp
--- a/src/hotspot/share/memory/metaspaceClosure.hpp
+++ b/src/hotspot/share/memory/metaspaceClosure.hpp
@@ -121,11 +121,11 @@
     virtual MetaspaceObj::Type msotype() const = 0;
     virtual bool is_read_only_by_default() const = 0;
     virtual ~Ref() {}
 
     address obj() const {
-      return *addr();
+      return address(intptr_t(*addr()) & ~0x3); // FIXME: just in case it is tagged
     }
 
     address* addr() const {
       return (address*)mpp();
     }
diff a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp
--- a/src/hotspot/share/memory/universe.cpp
+++ b/src/hotspot/share/memory/universe.cpp
@@ -62,10 +62,11 @@
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oopHandle.inline.hpp"
+#include "oops/trainingData.hpp"
 #include "oops/typeArrayKlass.hpp"
 #include "prims/resolvedMethodTable.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/cpuTimeCounters.hpp"
diff a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp
--- a/src/hotspot/share/memory/universe.hpp
+++ b/src/hotspot/share/memory/universe.hpp
@@ -233,11 +233,11 @@
   static oop          array_index_out_of_bounds_exception_instance();
   static oop          array_store_exception_instance();
   static oop          class_cast_exception_instance();
   static oop          vm_exception()                  { return internal_error_instance(); }
 
-  static Array<Klass*>* the_array_interfaces_array()  { return _the_array_interfaces_array; }
+  static Array<Klass*>* the_array_interfaces_array()  { return _the_array_interfaces_array;   }
   static uintx        the_array_interfaces_bitmap()   { return _the_array_interfaces_bitmap; }
 
   static Method*      finalizer_register_method();
   static Method*      loader_addClass_method();
   static Method*      throw_illegal_access_error();
diff a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp
--- a/src/hotspot/share/memory/virtualspace.cpp
+++ b/src/hotspot/share/memory/virtualspace.cpp
@@ -532,15 +532,15 @@
     NOT_AIX(os::vm_allocation_granularity());
 
   const size_t attach_point_alignment = lcm(alignment, os_attach_point_alignment);
 
   char *aligned_heap_base_min_address = (char *)align_up((void *)HeapBaseMinAddress, alignment);
-  size_t noaccess_prefix = ((aligned_heap_base_min_address + size) > (char*)OopEncodingHeapMax) ?
+  size_t noaccess_prefix = (((aligned_heap_base_min_address + size) > (char*)OopEncodingHeapMax) LP64_ONLY(|| UseCompatibleCompressedOops)) ?
     noaccess_prefix_size(alignment) : 0;
 
   // Attempt to alloc at user-given address.
-  if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) {
+  if (!FLAG_IS_DEFAULT(HeapBaseMinAddress) LP64_ONLY(|| UseCompatibleCompressedOops)) {
     try_reserve_heap(size + noaccess_prefix, alignment, page_size, aligned_heap_base_min_address);
     if (_base != aligned_heap_base_min_address) { // Enforce this exact address.
       release();
     }
   }
@@ -557,11 +557,11 @@
     // address. try_reserve_heap() always returns this allocated memory, as only here
     // the criteria for a good heap are checked.
 
     // Attempt to allocate so that we can run without base and scale (32-Bit unscaled compressed oops).
     // Give it several tries from top of range to bottom.
-    if (aligned_heap_base_min_address + size <= (char *)UnscaledOopHeapMax) {
+    if (aligned_heap_base_min_address + size <= (char *)UnscaledOopHeapMax LP64_ONLY(&& !UseCompatibleCompressedOops)) {
 
       // Calc address range within we try to attach (range of possible start addresses).
       char* const highest_start = align_down((char *)UnscaledOopHeapMax - size, attach_point_alignment);
       char* const lowest_start  = align_up(aligned_heap_base_min_address, attach_point_alignment);
       try_reserve_range(highest_start, lowest_start, attach_point_alignment,
@@ -570,11 +570,12 @@
 
     // zerobased: Attempt to allocate in the lower 32G.
     char *zerobased_max = (char *)OopEncodingHeapMax;
 
     // Give it several tries from top of range to bottom.
-    if (aligned_heap_base_min_address + size <= zerobased_max &&    // Zerobased theoretical possible.
+    if (LP64_ONLY(!UseCompatibleCompressedOops &&)
+        aligned_heap_base_min_address + size <= zerobased_max &&    // Zerobased theoretical possible.
         ((_base == nullptr) ||                        // No previous try succeeded.
          (_base + size > zerobased_max))) {        // Unscaled delivered an arbitrary address.
 
       // Calc address range within we try to attach (range of possible start addresses).
       char *const highest_start = align_down(zerobased_max - size, attach_point_alignment);
@@ -641,11 +642,11 @@
   guarantee(is_aligned(size, alignment), "set by caller");
 
   if (UseCompressedOops) {
 #ifdef _LP64
     initialize_compressed_heap(size, alignment, page_size);
-    if (_size > size) {
+    if (_size > size LP64_ONLY(|| UseCompatibleCompressedOops)) {
       // We allocated heap with noaccess prefix.
       // It can happen we get a zerobased/unscaled heap with noaccess prefix,
       // if we had to try at arbitrary address.
       establish_noaccess_prefix();
     }
diff a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp
--- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp
+++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp
@@ -394,10 +394,15 @@
                       reserved_rgn->mem_tag_name(), p2i(reserved_rgn->base()), reserved_rgn->size());
         assert(reserved_rgn->contain_region(base_addr, size), "Reserved heap region should contain this mapping region");
         return true;
       }
 
+      if (reserved_rgn->mem_tag() == mtCode) {
+        assert(reserved_rgn->contain_region(base_addr, size), "Reserved code region should contain this mapping region");
+        return true;
+      }
+
       // Print some more details. Don't use UL here to avoid circularities.
       tty->print_cr("Error: existing region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %u.\n"
                     "       new region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %u.",
                     p2i(reserved_rgn->base()), p2i(reserved_rgn->end()), (unsigned)reserved_rgn->mem_tag(),
                     p2i(base_addr), p2i(base_addr + size), (unsigned)mem_tag);
diff a/src/hotspot/share/oops/array.hpp b/src/hotspot/share/oops/array.hpp
--- a/src/hotspot/share/oops/array.hpp
+++ b/src/hotspot/share/oops/array.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2024, 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.
@@ -53,10 +53,15 @@
  private:
   NONCOPYABLE(Array);
 
   inline void* operator new(size_t size, ClassLoaderData* loader_data, int length, TRAPS) throw();
 
+  inline void* operator new(size_t size, ClassLoaderData* loader_data, int length) throw();
+
+// Work-around -- see JDK-8331086
+  inline void* operator new(size_t size, int length, MemTag flags) throw();
+
   static size_t byte_sizeof(int length, size_t elm_byte_size) {
     return sizeof(Array<T>) + MAX2(length - 1, 0) * elm_byte_size;
   }
   static size_t byte_sizeof(int length) { return byte_sizeof(length, sizeof(T)); }
 
diff a/src/hotspot/share/oops/array.inline.hpp b/src/hotspot/share/oops/array.inline.hpp
--- a/src/hotspot/share/oops/array.inline.hpp
+++ b/src/hotspot/share/oops/array.inline.hpp
@@ -35,6 +35,22 @@
   size_t word_size = Array::size(length);
   return (void*) Metaspace::allocate(loader_data, word_size,
                                      MetaspaceObj::array_type(sizeof(T)), false, THREAD);
 }
 
+template <typename T>
+inline void* Array<T>::operator new(size_t size, ClassLoaderData* loader_data, int length) throw() {
+  //assert(T is training data, "");
+  size_t word_size = Array::size(length);
+  return (void*) Metaspace::allocate(loader_data, word_size,
+                                     MetaspaceObj::array_type(sizeof(T)), false);
+}
+
+template <typename T>
+inline void* Array<T>::operator new(size_t size, int length, MemTag flags) throw() {
+  size = Array::size(length) * BytesPerWord;
+  void* p = AllocateHeap(size * BytesPerWord, flags);
+  memset(p, 0, size);
+  return p;
+}
+
 #endif // SHARE_OOPS_ARRAY_INLINE_HPP
diff a/src/hotspot/share/oops/compressedOops.cpp b/src/hotspot/share/oops/compressedOops.cpp
--- a/src/hotspot/share/oops/compressedOops.cpp
+++ b/src/hotspot/share/oops/compressedOops.cpp
@@ -47,10 +47,14 @@
 // ZeroBased - Use zero based compressed oops with encoding when
 //     NarrowOopHeapBaseMin + heap_size < 32Gb
 // HeapBased - Use compressed oops with heap base + encoding.
 void CompressedOops::initialize(const ReservedHeapSpace& heap_space) {
 #ifdef _LP64
+ if (UseCompatibleCompressedOops) {
+    set_shift(LogMinObjAlignmentInBytes);
+    set_base((address)heap_space.compressed_oop_base());
+ } else {
   // Subtract a page because something can get allocated at heap base.
   // This also makes implicit null checking work, because the
   // memory+1 page below heap_base needs to cause a signal.
   // See needs_explicit_null_check.
   // Only set the heap base for compressed oops because it indicates
@@ -63,10 +67,11 @@
     // Did reserve heap below 32Gb. Can use base == 0;
     set_base(nullptr);
   } else {
     set_base((address)heap_space.compressed_oop_base());
   }
+ }
 
   _heap_address_range = heap_space.region();
 
   LogTarget(Debug, gc, heap, coops) lt;
   if (lt.is_enabled()) {
diff a/src/hotspot/share/oops/constMethod.cpp b/src/hotspot/share/oops/constMethod.cpp
--- a/src/hotspot/share/oops/constMethod.cpp
+++ b/src/hotspot/share/oops/constMethod.cpp
@@ -418,11 +418,12 @@
 }
 
 void ConstMethod::metaspace_pointers_do(MetaspaceClosure* it) {
   log_trace(cds)("Iter(ConstMethod): %p", this);
 
-  if (!method()->method_holder()->is_rewritten()) {
+  if (constants()->pool_holder() != nullptr && !method()->method_holder()->is_rewritten()) {
+    // holder is null for MH intrinsic methods
     it->push(&_constants, MetaspaceClosure::_writable);
   } else {
     it->push(&_constants);
   }
   it->push(&_stackmap_data);
diff a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp
--- a/src/hotspot/share/oops/constantPool.cpp
+++ b/src/hotspot/share/oops/constantPool.cpp
@@ -459,10 +459,15 @@
         Handle refs_handle(THREAD, stom);  // must handleize.
         set_resolved_references(loader_data->add_handle(refs_handle));
       }
     }
   }
+
+  if (CDSConfig::is_dumping_final_static_archive() && resolved_references() != nullptr) {
+    objArrayOop scratch_references = oopFactory::new_objArray(vmClasses::Object_klass(), resolved_references()->length(), CHECK);
+    HeapShared::add_scratch_resolved_references(this, scratch_references);
+  }
 }
 
 void ConstantPool::remove_unshareable_info() {
   // Shared ConstantPools are in the RO region, so the _flags cannot be modified.
   // The _on_stack flag is used to prevent ConstantPools from deallocation during
@@ -504,10 +509,13 @@
 
   if (src_k->is_typeArray_klass()) {
     type = "prim";
   } else {
     InstanceKlass* src_ik = InstanceKlass::cast(src_k);
+    if (CDSConfig::is_dumping_final_static_archive() && src_ik->class_loader_data() == nullptr) {
+      return "unreg";
+    }
     oop loader = src_ik->class_loader();
     if (loader == nullptr) {
       type = "boot";
     } else if (loader == SystemDictionary::java_platform_loader()) {
       type = "plat";
diff a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp
--- a/src/hotspot/share/oops/cpCache.cpp
+++ b/src/hotspot/share/oops/cpCache.cpp
@@ -47,10 +47,11 @@
 #include "oops/constantPool.inline.hpp"
 #include "oops/cpCache.inline.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/method.inline.hpp"
 #include "oops/resolvedFieldEntry.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "oops/resolvedMethodEntry.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/arguments.hpp"
@@ -174,11 +175,12 @@
         method->name() != vmSymbols::object_initializer_name()) {
       do_resolve = false;
     }
     if (invoke_code == Bytecodes::_invokestatic) {
       assert(method->method_holder()->is_initialized() ||
-             method->method_holder()->is_reentrant_initialization(JavaThread::current()),
+             method->method_holder()->is_reentrant_initialization(JavaThread::current()) ||
+             (CDSConfig::is_dumping_archive() && VM_Version::supports_fast_class_init_checks()),
              "invalid class initialization state for invoke_static");
 
       if (!VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
         // Don't mark invokestatic to method as resolved if the holder class has not yet completed
         // initialization. An invokestatic must only proceed if the class is initialized, but if
@@ -416,11 +418,13 @@
   ConstantPool* src_cp =  ArchiveBuilder::current()->get_source_addr(cp);
   for (int i = 0; i < _resolved_field_entries->length(); i++) {
     ResolvedFieldEntry* rfi = _resolved_field_entries->adr_at(i);
     int cp_index = rfi->constant_pool_index();
     bool archived = false;
-    bool resolved = rfi->is_resolved(Bytecodes::_getfield)  ||
+    bool resolved = rfi->is_resolved(Bytecodes::_getstatic) ||
+                    rfi->is_resolved(Bytecodes::_putstatic) ||
+                    rfi->is_resolved(Bytecodes::_getfield)  ||
                     rfi->is_resolved(Bytecodes::_putfield);
     if (resolved && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
       rfi->mark_and_relocate();
       archived = true;
     } else {
@@ -452,16 +456,13 @@
     ResolvedMethodEntry* rme = _resolved_method_entries->adr_at(i);
     int cp_index = rme->constant_pool_index();
     bool archived = false;
     bool resolved = rme->is_resolved(Bytecodes::_invokevirtual)   ||
                     rme->is_resolved(Bytecodes::_invokespecial)   ||
+                  //rme->is_resolved(Bytecodes::_invokestatic)    || // FIXME -- leyden+JEP483 merge
                     rme->is_resolved(Bytecodes::_invokeinterface) ||
                     rme->is_resolved(Bytecodes::_invokehandle);
-
-    // Just for safety -- this should not happen, but do not archive if we ever see this.
-    resolved &= !(rme->is_resolved(Bytecodes::_invokestatic));
-
     if (resolved && can_archive_resolved_method(src_cp, rme)) {
       rme->mark_and_relocate(src_cp);
       archived = true;
     } else {
       rme->remove_unshareable_info();
@@ -555,11 +556,12 @@
 
   if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
     return false;
   }
 
-  if (method_entry->is_resolved(Bytecodes::_invokeinterface) ||
+  if (method_entry->is_resolved(Bytecodes::_invokestatic) ||
+      method_entry->is_resolved(Bytecodes::_invokeinterface) ||
       method_entry->is_resolved(Bytecodes::_invokevirtual) ||
       method_entry->is_resolved(Bytecodes::_invokespecial)) {
     return true;
   } else if (method_entry->is_resolved(Bytecodes::_invokehandle)) {
     if (CDSConfig::is_dumping_invokedynamic()) {
diff a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, 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.
@@ -44,10 +44,11 @@
 #include "code/codeCache.hpp"
 #include "code/dependencyContext.hpp"
 #include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
+#include "interpreter/bytecodeHistogram.hpp"
 #include "interpreter/bytecodeStream.hpp"
 #include "interpreter/oopMapCache.hpp"
 #include "interpreter/rewriter.hpp"
 #include "jvm.h"
 #include "jvmtifiles/jvmti.h"
@@ -72,10 +73,11 @@
 #include "oops/klass.inline.hpp"
 #include "oops/method.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/recordComponent.hpp"
 #include "oops/symbol.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiRedefineClasses.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "prims/methodComparator.hpp"
 #include "runtime/arguments.hpp"
@@ -87,10 +89,11 @@
 #include "runtime/javaThread.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/orderAccess.hpp"
 #include "runtime/os.inline.hpp"
 #include "runtime/reflection.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/synchronizer.hpp"
 #include "runtime/threads.hpp"
 #include "services/classLoadingService.hpp"
 #include "services/finalizerService.hpp"
 #include "services/threadService.hpp"
@@ -860,12 +863,12 @@
     assert(!is_in_error_state(), "sanity");
   }
 #endif
 
   set_init_thread(THREAD);
-  AOTClassInitializer::call_runtime_setup(THREAD, this);
   set_initialization_state_and_notify(fully_initialized, CHECK);
+  AOTClassInitializer::call_runtime_setup(THREAD, this);
 }
 #endif
 
 bool InstanceKlass::verify_code(TRAPS) {
   // 1) Verify the bytecodes
@@ -1055,15 +1058,16 @@
 
 // Now relocate and link method entry points after class is rewritten.
 // This is outside is_rewritten flag. In case of an exception, it can be
 // executed more than once.
 void InstanceKlass::link_methods(TRAPS) {
-  PerfTraceTime timer(ClassLoader::perf_ik_link_methods_time());
+  PerfTraceElapsedTime timer(ClassLoader::perf_ik_link_methods_time());
 
   int len = methods()->length();
   for (int i = len-1; i >= 0; i--) {
     methodHandle m(THREAD, methods()->at(i));
+    RuntimeUpcalls::install_upcalls(m);
 
     // Set up method entry points for compiler and interpreter    .
     m->link_method(m, CHECK);
   }
 }
@@ -1164,10 +1168,25 @@
 
   bool wait = false;
 
   JavaThread* jt = THREAD;
 
+  if (ForceProfiling) {
+    // Preallocate MDOs.
+    for (int i = 0; i < methods()->length(); i++) {
+      assert(!HAS_PENDING_EXCEPTION, "");
+      methodHandle m(THREAD, methods()->at(i));
+      Method::build_profiling_method_data(m, THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        ResourceMark rm;
+        log_warning(cds)("MDO preallocation failed for %s", external_name());
+        CLEAR_PENDING_EXCEPTION;
+        break;
+      }
+    }
+  }
+
   bool debug_logging_enabled = log_is_enabled(Debug, class, init);
 
   // refer to the JVM book page 47 for description of steps
   // Step 1
   {
@@ -1302,10 +1321,11 @@
 
   // Step 9
   if (!HAS_PENDING_EXCEPTION) {
     set_initialization_state_and_notify(fully_initialized, CHECK);
     debug_only(vtable().verify(tty, true);)
+    CompilationPolicy::replay_training_at_init(this, THREAD);
   }
   else {
     // Step 10 and 11
     Handle e(THREAD, PENDING_EXCEPTION);
     CLEAR_PENDING_EXCEPTION;
@@ -1332,11 +1352,10 @@
     }
   }
   DTRACE_CLASSINIT_PROBE_WAIT(end, -1, wait);
 }
 
-
 void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS) {
   Handle h_init_lock(THREAD, init_lock());
   if (h_init_lock() != nullptr) {
     ObjectLocker ol(h_init_lock, THREAD);
     set_init_thread(nullptr); // reset _init_thread before changing _init_state
@@ -1562,11 +1581,13 @@
   assert(!is_abstract() && !is_interface(), "Should not create this object");
   size_t size = size_helper();  // Query before forming handle.
   return (instanceOop)Universe::heap()->obj_allocate(this, size, CHECK_NULL);
 }
 
-instanceOop InstanceKlass::allocate_instance(oop java_class, TRAPS) {
+instanceOop InstanceKlass::allocate_instance(oop java_class,
+                                             const char* who,
+                                             TRAPS) {
   Klass* k = java_lang_Class::as_Klass(java_class);
   if (k == nullptr) {
     ResourceMark rm(THREAD);
     THROW_(vmSymbols::java_lang_InstantiationException(), nullptr);
   }
@@ -1630,12 +1651,10 @@
 
 ArrayKlass* InstanceKlass::array_klass_or_null() {
   return array_klass_or_null(1);
 }
 
-static int call_class_initializer_counter = 0;   // for debugging
-
 Method* InstanceKlass::class_initializer() const {
   Method* clinit = find_method(
       vmSymbols::class_initializer_name(), vmSymbols::void_method_signature());
   if (clinit != nullptr && clinit->has_valid_initializer_flags()) {
     return clinit;
@@ -1665,10 +1684,65 @@
   }
 #endif
 
   methodHandle h_method(THREAD, class_initializer());
   assert(!is_initialized(), "we cannot initialize twice");
+
+#if 0
+  // FIXME -- revive this code added to leyden/premain for <clinit> profiling
+  int init_id = log_class_init(THREAD, this);
+  if (h_method() != nullptr) {
+    JavaCallArguments args; // No arguments
+    JavaValue result(T_VOID);
+    InstanceKlass* outer = THREAD->set_class_being_initialized(this);
+    jlong bc_start = (CountBytecodesPerThread ? THREAD->bc_counter_value() : BytecodeCounter::counter_value());
+
+    elapsedTimer timer;
+    {
+      PerfPauseTimer pt(THREAD->current_rt_call_timer(), THREAD->profile_rt_calls());
+      PauseRuntimeCallProfiling prcp(THREAD, THREAD->profile_rt_calls());
+
+      timer.start();
+      JavaCalls::call(&result, h_method, &args, THREAD); // Static call (no args)
+      timer.stop();
+    }
+
+    jlong bc_end = (CountBytecodesPerThread ? THREAD->bc_counter_value() : BytecodeCounter::counter_value());
+
+    jlong bc_executed = (bc_end - bc_start);
+    if (UsePerfData && outer == nullptr) { // outermost clinit
+      THREAD->inc_clinit_bc_counter_value(bc_executed);
+      ClassLoader::perf_class_init_bytecodes_count()->inc(bc_executed);
+    }
+
+    THREAD->set_class_being_initialized(outer);
+
+    LogStreamHandle(Debug, init) log;
+    if (log.is_enabled()) {
+      ResourceMark rm(THREAD);
+      log.print("%d Initialized in %.3fms (total: " JLONG_FORMAT "ms); ",
+                init_id, timer.seconds() * 1000.0, ClassLoader::class_init_time_ms());
+      if (CountBytecodes || CountBytecodesPerThread) {
+        log.print("executed " JLONG_FORMAT " bytecodes; ", bc_executed);
+      }
+      name()->print_value_on(&log);
+      log.print_cr(" by thread " PTR_FORMAT " \"%s\" (" PTR_FORMAT ")",
+                   p2i(THREAD), THREAD->name(), p2i(this));
+    }
+  }
+  LogTarget(Info, class, init) lt;
+  if (lt.is_enabled()) {
+    ResourceMark rm(THREAD);
+    LogStream ls(lt);
+    ls.print("%d Initialized ", init_id);
+    name()->print_value_on(&ls);
+    ls.print_cr("%s (" PTR_FORMAT ") by thread \"%s\"",
+                h_method() == nullptr ? "(no method)" : "", p2i(this),
+                THREAD->name());
+  }
+#else
+  static int call_class_initializer_counter = 0;   // for debugging
   LogTarget(Info, class, init) lt;
   if (lt.is_enabled()) {
     ResourceMark rm(THREAD);
     LogStream ls(lt);
     ls.print("%d Initializing ", call_class_initializer_counter++);
@@ -1681,10 +1755,11 @@
     ThreadInClassInitializer ticl(THREAD, this); // Track class being initialized
     JavaCallArguments args; // No arguments
     JavaValue result(T_VOID);
     JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args)
   }
+#endif
 }
 
 // If a class that implements this interface is initialized, is the JVM required
 // to first execute a <clinit> method declared in this interface,
 // or (if also_check_supers==true) any of the super types of this interface?
@@ -2485,11 +2560,11 @@
   DependencyContext dep_context(&_dep_context, &_dep_context_last_cleaned);
   return dep_context;
 }
 
 void InstanceKlass::mark_dependent_nmethods(DeoptimizationScope* deopt_scope, KlassDepChange& changes) {
-  dependencies().mark_dependent_nmethods(deopt_scope, changes);
+  dependencies().mark_dependent_nmethods(deopt_scope, changes, this);
 }
 
 void InstanceKlass::add_dependent_nmethod(nmethod* nm) {
   dependencies().add_dependent_nmethod(nm);
 }
@@ -2622,16 +2697,16 @@
   it->push(&_record_components);
 }
 
 #if INCLUDE_CDS
 void InstanceKlass::remove_unshareable_info() {
-
   if (is_linked()) {
     assert(can_be_verified_at_dumptime(), "must be");
     // Remember this so we can avoid walking the hierarchy at runtime.
     set_verified_at_dump_time();
   }
+  _misc_flags.set_has_init_deps_processed(false);
 
   Klass::remove_unshareable_info();
 
   if (SystemDictionaryShared::has_class_failed_verification(this)) {
     // Classes are attempted to link during dumping and may fail,
@@ -2686,10 +2761,11 @@
     // clear _nest_host to ensure re-load at runtime
     _nest_host = nullptr;
   }
   init_shared_package_entry();
   _dep_context_last_cleaned = 0;
+  DEBUG_ONLY(_shared_class_load_count = 0);
 
   remove_unshareable_flags();
 }
 
 void InstanceKlass::remove_unshareable_flags() {
@@ -2796,21 +2872,26 @@
   if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation() && !is_value_based()) {
     set_is_value_based();
   }
 }
 
-// Check if a class or any of its supertypes has a version older than 50.
-// CDS will not perform verification of old classes during dump time because
-// without changing the old verifier, the verification constraint cannot be
-// retrieved during dump time.
-// Verification of archived old classes will be performed during run time.
 bool InstanceKlass::can_be_verified_at_dumptime() const {
+  if (CDSConfig::preserve_all_dumptime_verification_states(this)) {
+    return true;
+  }
+
   if (MetaspaceShared::is_in_shared_metaspace(this)) {
     // This is a class that was dumped into the base archive, so we know
     // it was verified at dump time.
     return true;
   }
+
+  // Check if a class or any of its supertypes has a version older than 50.
+  // CDS will not perform verification of old classes during dump time because
+  // without changing the old verifier, the verification constraint cannot be
+  // retrieved during dump time.
+  // Verification of archived old classes will be performed during run time.
   if (major_version() < 50 /*JAVA_6_VERSION*/) {
     return false;
   }
   if (java_super() != nullptr && !java_super()->can_be_verified_at_dumptime()) {
     return false;
@@ -2834,10 +2915,11 @@
     return ClassLoader::APP_LOADER;
   } else {
     return ClassLoader::OTHER;
   }
 }
+
 #endif // INCLUDE_CDS
 
 #if INCLUDE_JVMTI
 static void clear_all_breakpoints(Method* m) {
   m->clear_all_breakpoints();
@@ -3611,10 +3693,14 @@
 
 const char* InstanceKlass::init_state_name() const {
   return state_names[init_state()];
 }
 
+const char* InstanceKlass::state2name(ClassState s) {
+  return state_names[s];
+}
+
 void InstanceKlass::print_on(outputStream* st) const {
   assert(is_klass(), "must be klass");
   Klass::print_on(st);
 
   st->print(BULLET"instance size:     %d", size_helper());                        st->cr();
@@ -3940,10 +4026,27 @@
     } else {
       info_stream.print(" source: shared objects file");
     }
   }
 
+  info_stream.print(" loader:");
+  if (is_shared()) {
+    info_stream.print(" %s", SystemDictionaryShared::class_loader_name_for_shared((Klass*)this));
+  } else if (loader_data == ClassLoaderData::the_null_class_loader_data()) {
+    info_stream.print(" boot_loader");
+  } else {
+    oop class_loader = loader_data->class_loader();
+    if (class_loader != nullptr) {
+      info_stream.print(" %s", class_loader->klass()->external_name());
+      oop cl_name_and_id = java_lang_ClassLoader::nameAndId(class_loader);
+      if (cl_name_and_id != nullptr) {
+        info_stream.print(" %s", java_lang_String::as_utf8_string(cl_name_and_id));
+      }
+    } else {
+      info_stream.print(" null");
+    }
+  }
   msg.info("%s", info_stream.as_string());
 
   if (log_is_enabled(Debug, class, load)) {
     stringStream debug_stream;
 
diff a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -521,10 +521,12 @@
   bool is_reentrant_initialization(Thread *thread)  { return thread == _init_thread; }
   ClassState  init_state() const           { return Atomic::load_acquire(&_init_state); }
   const char* init_state_name() const;
   bool is_rewritten() const                { return _misc_flags.rewritten(); }
 
+  static const char* state2name(ClassState state);
+
   // is this a sealed class
   bool is_sealed() const;
 
   // defineClass specified verification
   bool should_verify_class() const         { return _misc_flags.should_verify_class(); }
@@ -806,11 +808,11 @@
   Array<AnnotationArray*>* fields_type_annotations() const {
     return (_annotations != nullptr) ? _annotations->fields_type_annotations() : nullptr;
   }
   // allocation
   instanceOop allocate_instance(TRAPS);
-  static instanceOop allocate_instance(oop cls, TRAPS);
+  static instanceOop allocate_instance(oop cls, const char* who, TRAPS);
 
   // additional member function to return a handle
   instanceHandle allocate_instance_handle(TRAPS);
 
   objArrayOop allocate_objArray(int n, int length, TRAPS);
@@ -1086,11 +1088,11 @@
   void fence_and_clear_init_lock();
 
   bool link_class_impl                           (TRAPS);
   bool verify_code                               (TRAPS);
   void initialize_impl                           (TRAPS);
-  void initialize_super_interfaces               (TRAPS);
+  void initialize_super_interfaces(TRAPS);
 
   void add_initialization_error(JavaThread* current, Handle exception);
   oop get_initialization_error(JavaThread* current);
 
   // find a local method (returns null if not found)
@@ -1125,10 +1127,17 @@
   void init_shared_package_entry();
   bool can_be_verified_at_dumptime() const;
   void compute_has_loops_flag_for_methods();
 #endif
 
+  bool     has_init_deps_processed() const { return _misc_flags.has_init_deps_processed(); }
+  void set_has_init_deps_processed() {
+    assert(is_initialized(), "");
+    assert(!has_init_deps_processed(), "already set"); // one-off action
+    _misc_flags.set_has_init_deps_processed(true);
+  }
+
   jint compute_modifier_flags() const;
 
 public:
   // JVMTI support
   jint jvmti_class_status() const;
diff a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp
--- a/src/hotspot/share/oops/instanceKlassFlags.hpp
+++ b/src/hotspot/share/oops/instanceKlassFlags.hpp
@@ -66,10 +66,11 @@
     status(is_being_redefined                , 1 << 0) /* True if the klass is being redefined */ \
     status(has_resolved_methods              , 1 << 1) /* True if the klass has resolved MethodHandle methods */ \
     status(has_been_redefined                , 1 << 2) /* class has been redefined */ \
     status(is_scratch_class                  , 1 << 3) /* class is the redefined scratch class */ \
     status(is_marked_dependent               , 1 << 4) /* class is the redefined scratch class */ \
+    status(has_init_deps_processed           , 1 << 5) /* all init dependencies are processed */ \
     /* end of list */
 
 #define IK_STATUS_ENUM_NAME(name, value)    _misc_##name = value,
   enum {
     IK_STATUS_DO(IK_STATUS_ENUM_NAME)
diff a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp
--- a/src/hotspot/share/oops/klass.cpp
+++ b/src/hotspot/share/oops/klass.cpp
@@ -390,11 +390,11 @@
   if (length >= SECONDARY_SUPERS_TABLE_SIZE) {
     return SECONDARY_SUPERS_BITMAP_FULL;
   }
 
   {
-    PerfTraceTime ptt(ClassLoader::perf_secondary_hash_time());
+    //PerfTraceTime ptt(ClassLoader::perf_secondary_hash_time()); // Leyden FIXME
 
     ResourceMark rm;
     uintx bitmap = SECONDARY_SUPERS_BITMAP_EMPTY;
     auto hashed_secondaries = new GrowableArray<Klass*>(SECONDARY_SUPERS_TABLE_SIZE,
                                                         SECONDARY_SUPERS_TABLE_SIZE, nullptr);
diff a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp
--- a/src/hotspot/share/oops/klass.hpp
+++ b/src/hotspot/share/oops/klass.hpp
@@ -600,10 +600,11 @@
   virtual ArrayKlass* array_klass_or_null() = 0;
 
   virtual oop protection_domain() const = 0;
 
   oop class_loader() const;
+  Symbol* class_loader_name_and_id() const;
 
   inline oop klass_holder() const;
 
   inline void keep_alive() const;
 
diff a/src/hotspot/share/oops/klass.inline.hpp b/src/hotspot/share/oops/klass.inline.hpp
--- a/src/hotspot/share/oops/klass.inline.hpp
+++ b/src/hotspot/share/oops/klass.inline.hpp
@@ -92,10 +92,14 @@
 
 inline oop Klass::class_loader() const {
   return class_loader_data()->class_loader();
 }
 
+inline Symbol* Klass::class_loader_name_and_id() const {
+  return class_loader_data()->name_and_id();
+}
+
 inline vtableEntry* Klass::start_of_vtable() const {
   return (vtableEntry*) ((address)this + in_bytes(vtable_start_offset()));
 }
 
 inline ByteSize Klass::vtable_start_offset() {
diff a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp
--- a/src/hotspot/share/oops/method.cpp
+++ b/src/hotspot/share/oops/method.cpp
@@ -58,10 +58,11 @@
 #include "oops/methodData.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/symbol.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/continuationEntry.hpp"
@@ -179,16 +180,17 @@
 
 char* Method::name_and_sig_as_C_string(Klass* klass, Symbol* method_name, Symbol* signature) {
   const char* klass_name = klass->external_name();
   int klass_name_len  = (int)strlen(klass_name);
   int method_name_len = method_name->utf8_length();
-  int len             = klass_name_len + 1 + method_name_len + signature->utf8_length();
+  int len             = klass_name_len + 2 + method_name_len + signature->utf8_length();
   char* dest          = NEW_RESOURCE_ARRAY(char, len + 1);
   strcpy(dest, klass_name);
-  dest[klass_name_len] = '.';
-  strcpy(&dest[klass_name_len + 1], method_name->as_C_string());
-  strcpy(&dest[klass_name_len + 1 + method_name_len], signature->as_C_string());
+  dest[klass_name_len + 0] = ':';
+  dest[klass_name_len + 1] = ':';
+  strcpy(&dest[klass_name_len + 2], method_name->as_C_string());
+  strcpy(&dest[klass_name_len + 2 + method_name_len], signature->as_C_string());
   dest[len] = 0;
   return dest;
 }
 
 char* Method::name_and_sig_as_C_string(Klass* klass, Symbol* method_name, Symbol* signature, char* buf, int size) {
@@ -383,13 +385,18 @@
 Symbol* Method::klass_name() const {
   return method_holder()->name();
 }
 
 void Method::metaspace_pointers_do(MetaspaceClosure* it) {
-  log_trace(cds)("Iter(Method): %p", this);
-
-  if (!method_holder()->is_rewritten()) {
+  LogStreamHandle(Trace, cds) lsh;
+  if (lsh.is_enabled()) {
+    lsh.print("Iter(Method): %p ", this);
+    print_external_name(&lsh);
+    lsh.cr();
+  }
+  if (method_holder() != nullptr && !method_holder()->is_rewritten()) {
+    // holder is null for MH intrinsic methods
     it->push(&_constMethod, MetaspaceClosure::_writable);
   } else {
     it->push(&_constMethod);
   }
   it->push(&_method_data);
@@ -403,16 +410,29 @@
 // where they should point in a new JVM.  Further initialize some
 // entries now in order allow them to be write protected later.
 
 void Method::remove_unshareable_info() {
   unlink_method();
+  if (method_data() != nullptr) {
+    method_data()->remove_unshareable_info();
+  }
+  if (method_counters() != nullptr) {
+    method_counters()->remove_unshareable_info();
+  }
   JFR_ONLY(REMOVE_METHOD_ID(this);)
 }
 
 void Method::restore_unshareable_info(TRAPS) {
   assert(is_method() && is_valid_method(this), "ensure C++ vtable is restored");
+  if (method_data() != nullptr) {
+    method_data()->restore_unshareable_info(CHECK);
+  }
+  if (method_counters() != nullptr) {
+    method_counters()->restore_unshareable_info(CHECK);
+  }
   assert(!queued_for_compilation(), "method's queued_for_compilation flag should not be set");
+  assert(!pending_queue_processed(), "method's pending_queued_processed flag should not be set");
 }
 #endif
 
 void Method::set_vtable_index(int index) {
   if (is_shared() && !MetaspaceShared::remapped_readwrite() && method_holder()->verified_at_dump_time()) {
@@ -575,13 +595,44 @@
     st->print_cr ("  compiled_invocation_count:    " INT64_FORMAT_W(11), compiled_invocation_count());
   }
 #endif
 }
 
+MethodTrainingData* Method::training_data_or_null() const {
+  MethodCounters* mcs = method_counters();
+  if (mcs == nullptr) {
+    return nullptr;
+  } else {
+    return mcs->method_training_data();
+  }
+}
+
+bool Method::init_training_data(MethodTrainingData* tdata) {
+  MethodCounters* mcs = method_counters();
+  if (mcs == nullptr) {
+    return false;
+  } else {
+    return mcs->init_method_training_data(tdata);
+  }
+}
+
+bool Method::install_training_method_data(const methodHandle& method) {
+  MethodTrainingData* mtd = MethodTrainingData::find(method);
+  if (mtd != nullptr && mtd->has_holder() && mtd->final_profile() != nullptr &&
+      mtd->holder() == method() && mtd->final_profile()->method() == method()) { // FIXME
+    Atomic::replace_if_null(&method->_method_data, mtd->final_profile());
+    return true;
+  }
+  return false;
+}
+
 // Build a MethodData* object to hold profiling information collected on this
 // method when requested.
 void Method::build_profiling_method_data(const methodHandle& method, TRAPS) {
+  if (install_training_method_data(method)) {
+    return;
+  }
   // Do not profile the method if metaspace has hit an OOM previously
   // allocating profiling data. Callers clear pending exception so don't
   // add one here.
   if (ClassLoaderDataGraph::has_metaspace_oom()) {
     return;
@@ -598,11 +649,24 @@
   if (!Atomic::replace_if_null(&method->_method_data, method_data)) {
     MetadataFactory::free_metadata(loader_data, method_data);
     return;
   }
 
-  if (PrintMethodData && (Verbose || WizardMode)) {
+  /*
+  LogStreamHandle(Info, mdo) lsh;
+  if (lsh.is_enabled()) {
+    ResourceMark rm(THREAD);
+    lsh.print("build_profiling_method_data for ");
+    method->print_name(&lsh);
+    lsh.cr();
+  }
+  */
+  if (ForceProfiling && TrainingData::need_data()) {
+    MethodTrainingData* mtd = MethodTrainingData::make(method, false);
+    guarantee(mtd != nullptr, "");
+  }
+  if (PrintMethodData) {
     ResourceMark rm(THREAD);
     tty->print("build_profiling_method_data for ");
     method->print_name(tty);
     tty->cr();
     // At the end of the run, the MDO, full of data, will be dumped.
@@ -639,10 +703,15 @@
 
   if (!mh->init_method_counters(counters)) {
     MetadataFactory::free_metadata(mh->method_holder()->class_loader_data(), counters);
   }
 
+  if (ForceProfiling && TrainingData::need_data()) {
+    MethodTrainingData* mtd = MethodTrainingData::make(mh, false);
+    guarantee(mtd != nullptr, "");
+  }
+
   return mh->method_counters();
 }
 
 bool Method::init_method_counters(MethodCounters* counters) {
   // Try to install a pointer to MethodCounters, return true on success.
@@ -865,10 +934,15 @@
 
 bool Method::needs_clinit_barrier() const {
   return is_static() && !method_holder()->is_initialized();
 }
 
+bool Method::code_has_clinit_barriers() const {
+  nmethod* nm = code();
+  return (nm != nullptr) && nm->has_clinit_barriers();
+}
+
 bool Method::is_object_wait0() const {
   return klass_name() == vmSymbols::java_lang_Object()
          && name() == vmSymbols::wait_name();
 }
 
@@ -1149,10 +1223,16 @@
   }
   NOT_PRODUCT(set_compiled_invocation_count(0);)
 
   clear_method_data();
   clear_method_counters();
+  clear_is_not_c1_compilable();
+  clear_is_not_c1_osr_compilable();
+  clear_is_not_c2_compilable();
+  clear_is_not_c2_osr_compilable();
+  clear_queued_for_compilation();
+  set_pending_queue_processed(false);
   remove_unshareable_flags();
 }
 
 void Method::remove_unshareable_flags() {
   // clear all the flags that shouldn't be in the archived version
@@ -1160,14 +1240,17 @@
   assert(!is_obsolete(), "must be");
   assert(!is_deleted(), "must be");
 
   set_is_prefixed_native(false);
   set_queued_for_compilation(false);
+  set_pending_queue_processed(false);
   set_is_not_c2_compilable(false);
   set_is_not_c1_compilable(false);
   set_is_not_c2_osr_compilable(false);
   set_on_stack_flag(false);
+  set_has_upcall_on_method_entry(false);
+  set_has_upcall_on_method_exit(false);
 }
 #endif
 
 // Called when the method_holder is getting linked. Setup entrypoints so the method
 // is ready to be called from interpreter, compiler, and vtables.
@@ -1222,14 +1305,19 @@
         THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "Initial size of CodeCache is too small");
       }
       assert(_from_interpreted_entry == get_i2c_entry(), "invariant");
     }
   }
+  if (_preload_code != nullptr) {
+    MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
+    set_code(h_method, _preload_code);
+    assert(((nmethod*)_preload_code)->scc_entry() == _scc_entry, "sanity");
+  }
 }
 
 address Method::make_adapters(const methodHandle& mh, TRAPS) {
-  PerfTraceTime timer(ClassLoader::perf_method_adapters_time());
+  PerfTraceElapsedTime timer(ClassLoader::perf_method_adapters_time());
 
   // Adapters for compiled code are made eagerly here.  They are fairly
   // small (generally < 100 bytes) and quick to make (and cached and shared)
   // so making them eagerly shouldn't be too expensive.
   AdapterHandlerEntry* adapter = AdapterHandlerLibrary::get_adapter(mh);
@@ -1944,12 +2032,14 @@
   }
 }
 
 int Method::highest_comp_level() const {
   const MethodCounters* mcs = method_counters();
+  nmethod* nm = code();
+  int level = (nm != nullptr) ? nm->comp_level() : CompLevel_none;
   if (mcs != nullptr) {
-    return mcs->highest_comp_level();
+    return MAX2(mcs->highest_comp_level(), level);
   } else {
     return CompLevel_none;
   }
 }
 
diff a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp
--- a/src/hotspot/share/oops/method.hpp
+++ b/src/hotspot/share/oops/method.hpp
@@ -58,14 +58,16 @@
 class CheckedExceptionElement;
 class LocalVariableTableElement;
 class AdapterHandlerEntry;
 class MethodData;
 class MethodCounters;
+class MethodTrainingData;
 class ConstMethod;
 class InlineTableSizes;
 class nmethod;
 class InterpreterOopMap;
+class SCCEntry;
 
 class Method : public Metadata {
  friend class VMStructs;
  friend class JVMCIVMStructs;
  friend class MethodTest;
@@ -100,10 +102,13 @@
   // time (whenever a compile completes).  It can transition from not-null to
   // null only at safepoints (because of a de-opt).
   nmethod* volatile _code;                   // Points to the corresponding piece of native code
   volatile address  _from_interpreted_entry; // Cache of _code ? _adapter->i2c_entry() : _i2i_entry
 
+  nmethod*  _preload_code;  // preloaded SCCache code
+  SCCEntry* _scc_entry;     // SCCache entry for pre-loading code
+
   // Constructor
   Method(ConstMethod* xconst, AccessFlags access_flags, Symbol* name);
  public:
 
   static Method* allocate(ClassLoaderData* loader_data,
@@ -313,10 +318,15 @@
   // method data access
   MethodData* method_data() const              {
     return _method_data;
   }
 
+  void set_method_data(MethodData* data);
+
+  MethodTrainingData* training_data_or_null() const;
+  bool init_training_data(MethodTrainingData* tdata);
+
   // mark an exception handler as entered (used to prune dead catch blocks in C2)
   void set_exception_handler_entered(int handler_bci);
 
   MethodCounters* method_counters() const {
     return _method_counters;
@@ -340,11 +350,11 @@
 
   bool was_executed_more_than(int n);
   bool was_never_executed()                     { return !was_executed_more_than(0);  }
 
   static void build_profiling_method_data(const methodHandle& method, TRAPS);
-
+  static bool install_training_method_data(const methodHandle& method);
   static MethodCounters* build_method_counters(Thread* current, Method* m);
 
   int interpreter_invocation_count()            { return invocation_count();          }
 
 #ifndef PRODUCT
@@ -380,10 +390,20 @@
   }
   void set_from_compiled_entry(address entry) {
     _from_compiled_entry =  entry;
   }
 
+  void set_preload_code(nmethod* code) {
+    _preload_code = code;
+  }
+  void set_scc_entry(SCCEntry* entry) {
+    _scc_entry = entry;
+  }
+  SCCEntry* scc_entry() const {
+    return _scc_entry;
+  }
+
   address get_i2c_entry();
   address get_c2i_entry();
   address get_c2i_unverified_entry();
   address get_c2i_no_clinit_check_entry();
   AdapterHandlerEntry* adapter() const {
@@ -594,10 +614,11 @@
   // NOTE: code() is inherently racy as deopt can be clearing code
   // simultaneously. Use with caution.
   bool has_compiled_code() const;
 
   bool needs_clinit_barrier() const;
+  bool code_has_clinit_barriers() const;
 
   // sizing
   static int header_size()                       {
     return align_up((int)sizeof(Method), wordSize) / wordSize;
   }
diff a/src/hotspot/share/oops/methodCounters.cpp b/src/hotspot/share/oops/methodCounters.cpp
--- a/src/hotspot/share/oops/methodCounters.cpp
+++ b/src/hotspot/share/oops/methodCounters.cpp
@@ -21,17 +21,23 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/cdsConfig.hpp"
 #include "compiler/compiler_globals.hpp"
 #include "compiler/compilerOracle.hpp"
+#include "memory/metaspaceClosure.hpp"
 #include "oops/method.hpp"
 #include "oops/methodCounters.hpp"
+#include "oops/trainingData.hpp"
 #include "runtime/handles.inline.hpp"
+#include "memory/resourceArea.hpp"
 
 MethodCounters::MethodCounters(const methodHandle& mh) :
+  _method(mh()),
+  _method_training_data(nullptr),
   _prev_time(0),
   _rate(0),
   _highest_comp_level(0),
   _highest_osr_comp_level(0)
 {
@@ -46,10 +52,14 @@
 
   _invoke_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift;
   _backedge_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift;
 }
 
+MethodCounters::MethodCounters() {
+  assert(CDSConfig::is_dumping_static_archive() || UseSharedSpaces, "only for CDS");
+}
+
 MethodCounters* MethodCounters::allocate_no_exception(const methodHandle& mh) {
   ClassLoaderData* loader_data = mh->method_holder()->class_loader_data();
   return new(loader_data, method_counters_size(), MetaspaceObj::MethodCountersType) MethodCounters(mh);
 }
 
@@ -67,10 +77,50 @@
   set_rate(0);
   set_highest_comp_level(0);
   set_highest_osr_comp_level(0);
 }
 
+void MethodCounters::metaspace_pointers_do(MetaspaceClosure* it) {
+  log_trace(cds)("Iter(MethodCounters): %p", this);
+  it->push(&_method);
+  it->push(&_method_training_data);
+}
+
+#if INCLUDE_CDS
+void MethodCounters::remove_unshareable_info() {
+  _method_training_data = nullptr;
+}
+
+void MethodCounters::restore_unshareable_info(TRAPS) {
+}
+#endif // INCLUDE_CDS
+
+void MethodCounters::print_on(outputStream* st) const {
+  assert(is_methodCounters(), "should be method counters");
+  st->print("method counters");
+  print_data_on(st);
+}
+
+void MethodCounters::print_data_on(outputStream* st) const {
+  ResourceMark rm;
+  st->print_cr("  - invocation_counter: %d carry=%d", _invocation_counter.count(), _invocation_counter.carry());
+  st->print_cr("  - backedge_counter: %d carry=%d",   _backedge_counter.count(), _backedge_counter.carry());
+  st->print_cr("  - prev_time: " JLONG_FORMAT,        _prev_time);
+  st->print_cr("  - rate: %.3f",             _rate);
+  st->print_cr("  - invoke_mask: %d",        _invoke_mask);
+  st->print_cr("  - backedge_mask: %d",      _backedge_mask);
+  st->print_cr("  - prev_event_count: %d",   _prev_event_count);
+#if COMPILER2_OR_JVMCI
+  st->print_cr("  - interpreter_throwout_count: %u", _interpreter_throwout_count);
+#endif
+#if INCLUDE_JVMTI
+  st->print_cr("  - number_of_breakpoints: %u", _number_of_breakpoints);
+#endif
+  st->print_cr("  - highest_comp_level: %u", _highest_comp_level);
+  st->print_cr("  - highest_osr_comp_level: %u", _highest_osr_comp_level);
+}
+
 void MethodCounters::print_value_on(outputStream* st) const {
   assert(is_methodCounters(), "must be methodCounters");
   st->print("method counters");
   print_address_on(st);
 }
diff a/src/hotspot/share/oops/methodCounters.hpp b/src/hotspot/share/oops/methodCounters.hpp
--- a/src/hotspot/share/oops/methodCounters.hpp
+++ b/src/hotspot/share/oops/methodCounters.hpp
@@ -28,34 +28,51 @@
 #include "oops/metadata.hpp"
 #include "compiler/compilerDefinitions.hpp"
 #include "interpreter/invocationCounter.hpp"
 #include "utilities/align.hpp"
 
+class MethodTrainingData;
+
 class MethodCounters : public Metadata {
  friend class VMStructs;
  friend class JVMCIVMStructs;
+
+ // Used by CDS. These classes need to access the private default constructor.
+ template <class T> friend class CppVtableTesterA;
+ template <class T> friend class CppVtableTesterB;
+ template <class T> friend class CppVtableCloner;
+
  private:
-  InvocationCounter _invocation_counter;         // Incremented before each activation of the method - used to trigger frequency-based optimizations
-  InvocationCounter _backedge_counter;           // Incremented before each backedge taken - used to trigger frequency-based optimizations
-  jlong             _prev_time;                   // Previous time the rate was acquired
-  float             _rate;                        // Events (invocation and backedge counter increments) per millisecond
-  int               _invoke_mask;                 // per-method Tier0InvokeNotifyFreqLog
-  int               _backedge_mask;               // per-method Tier0BackedgeNotifyFreqLog
-  int               _prev_event_count;            // Total number of events saved at previous callback
+  InvocationCounter   _invocation_counter;         // Incremented before each activation of the method - used to trigger frequency-based optimizations
+  InvocationCounter   _backedge_counter;           // Incremented before each backedge taken - used to trigger frequency-based optimizations
+
+  // Back pointer to the Method*
+  Method* _method;
+
+  MethodTrainingData* _method_training_data;
+  jlong               _prev_time;                   // Previous time the rate was acquired
+  float               _rate;                        // Events (invocation and backedge counter increments) per millisecond
+  int                 _invoke_mask;                 // per-method Tier0InvokeNotifyFreqLog
+  int                 _backedge_mask;               // per-method Tier0BackedgeNotifyFreqLog
+  int                 _prev_event_count;            // Total number of events saved at previous callback
 #if COMPILER2_OR_JVMCI
-  u2                _interpreter_throwout_count; // Count of times method was exited via exception while interpreting
+  u2                  _interpreter_throwout_count; // Count of times method was exited via exception while interpreting
 #endif
 #if INCLUDE_JVMTI
-  u2                _number_of_breakpoints;      // fullspeed debugging support
+  u2                  _number_of_breakpoints;      // fullspeed debugging support
 #endif
-  u1                _highest_comp_level;          // Highest compile level this method has ever seen.
-  u1                _highest_osr_comp_level;      // Same for OSR level
+  u1                  _highest_comp_level;          // Highest compile level this method has ever seen.
+  u1                  _highest_osr_comp_level;      // Same for OSR level
 
   MethodCounters(const methodHandle& mh);
+  MethodCounters();
+
  public:
   virtual bool is_methodCounters() const { return true; }
 
+  Method* method() const { return _method; }
+
   static MethodCounters* allocate_no_exception(const methodHandle& mh);
   static MethodCounters* allocate_with_exception(const methodHandle& mh, TRAPS);
 
   void deallocate_contents(ClassLoaderData* loader_data) {}
 
@@ -64,10 +81,12 @@
   }
   virtual int size() const {
     return method_counters_size();
   }
   MetaspaceObj::Type type() const { return MethodCountersType; }
+  virtual void metaspace_pointers_do(MetaspaceClosure* iter);
+
   void clear_counters();
 
 #if COMPILER2_OR_JVMCI
   void interpreter_throwout_increment() {
     if (_interpreter_throwout_count < 65534) {
@@ -127,9 +146,23 @@
   static ByteSize backedge_mask_offset() {
     return byte_offset_of(MethodCounters, _backedge_mask);
   }
 
   virtual const char* internal_name() const { return "{method counters}"; }
-  virtual void print_value_on(outputStream* st) const;
 
+  MethodTrainingData* method_training_data() const { return _method_training_data; }
+  bool init_method_training_data(MethodTrainingData* tdata) {
+    return (_method_training_data == tdata ||
+            Atomic::replace_if_null(&_method_training_data, tdata));
+  }
+
+#if INCLUDE_CDS
+  void remove_unshareable_info();
+  void restore_unshareable_info(TRAPS);
+#endif
+
+  // Printing
+  void print_on      (outputStream* st) const;
+  void print_value_on(outputStream* st) const;
+  void print_data_on(outputStream* st) const;
 };
 #endif // SHARE_OOPS_METHODCOUNTERS_HPP
diff a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp
--- a/src/hotspot/share/oops/methodData.cpp
+++ b/src/hotspot/share/oops/methodData.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/cdsConfig.hpp"
 #include "ci/ciMethodData.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "compiler/compilationPolicy.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/compilerOracle.hpp"
@@ -321,24 +322,50 @@
 
 void TypeStackSlotEntries::clean_weak_klass_links(bool always_clean) {
   for (int i = 0; i < _number_of_entries; i++) {
     intptr_t p = type(i);
     Klass* k = (Klass*)klass_part(p);
-    if (k != nullptr && (always_clean || !k->is_loader_alive())) {
-      set_type(i, with_status((Klass*)nullptr, p));
+    if (k != nullptr) {
+      if (!always_clean && k->is_instance_klass() && InstanceKlass::cast(k)->is_not_initialized()) {
+        continue; // skip not-yet-initialized classes // TODO: maybe clear the slot instead?
+      }
+      if (always_clean || !k->is_loader_alive()) {
+        set_type(i, with_status((Klass*)nullptr, p));
+      }
     }
   }
 }
 
+void TypeStackSlotEntries::metaspace_pointers_do(MetaspaceClosure* it) {
+  for (int i = 0; i < _number_of_entries; i++) {
+    set_type(i, klass_part(type(i))); // reset tag; FIXME: properly handle tagged pointers
+    Klass** k = (Klass**)type_adr(i);
+    it->push(k);
+//    it->push_tagged(k);
+  }
+}
+
 void ReturnTypeEntry::clean_weak_klass_links(bool always_clean) {
   intptr_t p = type();
   Klass* k = (Klass*)klass_part(p);
-  if (k != nullptr && (always_clean || !k->is_loader_alive())) {
-    set_type(with_status((Klass*)nullptr, p));
+  if (k != nullptr) {
+    if (!always_clean && k->is_instance_klass() && InstanceKlass::cast(k)->is_not_initialized()) {
+      return; // skip not-yet-initialized classes // TODO: maybe clear the slot instead?
+    }
+    if (always_clean || !k->is_loader_alive()) {
+      set_type(with_status((Klass*)nullptr, p));
+    }
   }
 }
 
+void ReturnTypeEntry::metaspace_pointers_do(MetaspaceClosure* it) {
+  Klass** k = (Klass**)type_adr(); // tagged
+  set_type(klass_part(type())); // reset tag; FIXME: properly handle tagged pointers
+  it->push(k);
+//  it->push_tagged(k);
+}
+
 bool TypeEntriesAtCall::return_profiling_enabled() {
   return MethodData::profile_return();
 }
 
 bool TypeEntriesAtCall::arguments_profiling_enabled() {
@@ -410,16 +437,28 @@
 // which are used to store a type profile for the receiver of the check.
 
 void ReceiverTypeData::clean_weak_klass_links(bool always_clean) {
     for (uint row = 0; row < row_limit(); row++) {
     Klass* p = receiver(row);
-    if (p != nullptr && (always_clean || !p->is_loader_alive())) {
-      clear_row(row);
+    if (p != nullptr) {
+      if (!always_clean && p->is_instance_klass() && InstanceKlass::cast(p)->is_not_initialized()) {
+        continue; // skip not-yet-initialized classes // TODO: maybe clear the slot instead?
+      }
+      if (always_clean || !p->is_loader_alive()) {
+        clear_row(row);
+      }
     }
   }
 }
 
+void ReceiverTypeData::metaspace_pointers_do(MetaspaceClosure *it) {
+  for (uint row = 0; row < row_limit(); row++) {
+    Klass** recv = (Klass**)intptr_at_adr(receiver_cell_index(row));
+    it->push(recv);
+  }
+}
+
 void ReceiverTypeData::print_receiver_data_on(outputStream* st) const {
   uint row;
   int entries = 0;
   for (row = 0; row < row_limit(); row++) {
     if (receiver(row) != nullptr)  entries++;
@@ -644,10 +683,15 @@
   tab(st);
   _parameters.print_data_on(st);
   st->cr();
 }
 
+void SpeculativeTrapData::metaspace_pointers_do(MetaspaceClosure* it) {
+  Method** m = (Method**)intptr_at_adr(speculative_trap_method);
+  it->push(m);
+}
+
 void SpeculativeTrapData::print_data_on(outputStream* st, const char* extra) const {
   print_shared(st, "SpeculativeTrapData", extra);
   tab(st);
   method()->print_short_name(st);
   st->cr();
@@ -1221,14 +1265,18 @@
 
 // Initialize the MethodData* corresponding to a given method.
 MethodData::MethodData(const methodHandle& method)
   : _method(method()),
     // Holds Compile_lock
-    _extra_data_lock(Mutex::nosafepoint, "MDOExtraData_lock"),
     _compiler_counters(),
     _parameters_type_data_di(parameters_uninitialized) {
-  initialize();
+    _extra_data_lock = nullptr;
+    initialize();
+}
+
+MethodData::MethodData() {
+  assert(CDSConfig::is_dumping_static_archive() || UseSharedSpaces, "only for CDS");
 }
 
 // Reinitialize the storage of an existing MDO at a safepoint.  Doing it this way will ensure it's
 // not being accessed while the contents are being rewritten.
 class VM_ReinitializeMDO: public VM_Operation {
@@ -1368,11 +1416,11 @@
 int MethodData::mileage_of(Method* method) {
   return MAX2(method->invocation_count(), method->backedge_count());
 }
 
 bool MethodData::is_mature() const {
-  return CompilationPolicy::is_mature(_method);
+  return CompilationPolicy::is_mature((MethodData*)this);
 }
 
 // Translate a bci to its corresponding data index (di).
 address MethodData::bci_to_dp(int bci) {
   ResourceMark rm;
@@ -1556,11 +1604,12 @@
   st->print("method data for ");
   method()->print_value_on(st);
 }
 
 void MethodData::print_data_on(outputStream* st) const {
-  ConditionalMutexLocker ml(extra_data_lock(), !extra_data_lock()->owned_by_self(),
+  Mutex* lock = const_cast<MethodData*>(this)->extra_data_lock();
+  ConditionalMutexLocker ml(lock, !lock->owned_by_self(),
                             Mutex::_no_safepoint_check_flag);
   ResourceMark rm;
   ProfileData* data = first_data();
   if (_parameters_type_data_di != no_parameters) {
     parameters_type_data()->print_data_on(st);
@@ -1729,12 +1778,30 @@
   assert(profile_parameters_jsr292_only(), "inconsistent");
   return m->is_compiled_lambda_form();
 }
 
 void MethodData::metaspace_pointers_do(MetaspaceClosure* it) {
-  log_trace(cds)("Iter(MethodData): %p", this);
+  log_trace(cds)("Iter(MethodData): %p for %p %s", this, _method, _method->name_and_sig_as_C_string());
   it->push(&_method);
+  if (_parameters_type_data_di != no_parameters) {
+    parameters_type_data()->metaspace_pointers_do(it);
+  }
+  for (ProfileData* data = first_data(); is_valid(data); data = next_data(data)) {
+    data->metaspace_pointers_do(it);
+  }
+  for (DataLayout* dp = extra_data_base();
+                   dp < extra_data_limit();
+                   dp = MethodData::next_extra(dp)) {
+    if (dp->tag() == DataLayout::speculative_trap_data_tag) {
+      ResourceMark rm;
+      SpeculativeTrapData* data = new SpeculativeTrapData(dp);
+      data->metaspace_pointers_do(it);
+    } else if (dp->tag() == DataLayout::no_tag ||
+               dp->tag() == DataLayout::arg_info_data_tag) {
+      break;
+    }
+  }
 }
 
 void MethodData::clean_extra_data_helper(DataLayout* dp, int shift, bool reset) {
   check_extra_data_locked();
 
@@ -1762,10 +1829,13 @@
 class CleanExtraDataKlassClosure : public CleanExtraDataClosure {
   bool _always_clean;
 public:
   CleanExtraDataKlassClosure(bool always_clean) : _always_clean(always_clean) {}
   bool is_live(Method* m) {
+    if (!_always_clean && m->method_holder()->is_instance_klass() && InstanceKlass::cast(m->method_holder())->is_not_initialized()) {
+      return true; // TODO: treat as unloaded instead?
+    }
     return !(_always_clean) && m->method_holder()->is_loader_alive();
   }
 };
 
 // Check for entries that reference a redefined method
@@ -1773,10 +1843,23 @@
 public:
   CleanExtraDataMethodClosure() {}
   bool is_live(Method* m) { return !m->is_old(); }
 };
 
+Mutex* MethodData::extra_data_lock() {
+  Mutex* lock = Atomic::load(&_extra_data_lock);
+  if (lock == nullptr) {
+    lock = new Mutex(Mutex::nosafepoint, "MDOExtraData_lock");
+    Mutex* old = Atomic::cmpxchg(&_extra_data_lock, (Mutex*)nullptr, lock);
+    if (old != nullptr) {
+      // Another thread created the lock before us. Use that lock instead.
+      delete lock;
+      return old;
+    }
+  }
+  return lock;
+}
 
 // Remove SpeculativeTrapData entries that reference an unloaded or
 // redefined method
 void MethodData::clean_extra_data(CleanExtraDataClosure* cl) {
   check_extra_data_locked();
@@ -1893,17 +1976,27 @@
 #if INCLUDE_JVMCI
   FailedSpeculation::free_failed_speculations(get_failed_speculations_address());
 #endif
 }
 
+#if INCLUDE_CDS
+void MethodData::remove_unshareable_info() {
+  _extra_data_lock = nullptr;
+}
+
+void MethodData::restore_unshareable_info(TRAPS) {
+  //_extra_data_lock = new Mutex(Mutex::nosafepoint, "MDOExtraData_lock");
+}
+#endif // INCLUDE_CDS
+       
 #ifdef ASSERT
 void MethodData::check_extra_data_locked() const {
     // Cast const away, just to be able to verify the lock
     // Usually we only want non-const accesses on the lock,
     // so this here is an exception.
     MethodData* self = (MethodData*)this;
-    assert(self->extra_data_lock()->owned_by_self(), "must have lock");
+    assert(self->extra_data_lock()->owned_by_self() || CDSConfig::is_dumping_archive(), "must have lock");
     assert(!Thread::current()->is_Java_thread() ||
            JavaThread::current()->is_in_no_safepoint_scope(),
            "JavaThread must have NoSafepointVerifier inside lock scope");
 }
 #endif
diff a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp
--- a/src/hotspot/share/oops/methodData.hpp
+++ b/src/hotspot/share/oops/methodData.hpp
@@ -27,11 +27,10 @@
 
 #include "interpreter/bytecodes.hpp"
 #include "interpreter/invocationCounter.hpp"
 #include "oops/metadata.hpp"
 #include "oops/method.hpp"
-#include "oops/oop.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/mutex.hpp"
 #include "utilities/align.hpp"
 #include "utilities/copy.hpp"
@@ -201,10 +200,13 @@
   }
   void release_set_cell_at(int index, intptr_t value);
   intptr_t cell_at(int index) const {
     return _cells[index];
   }
+  intptr_t* cell_at_adr(int index) const {
+    return const_cast<intptr_t*>(&_cells[index]);
+  }
 
   bool set_flag_at(u1 flag_number) {
     const u1 bit = 1 << flag_number;
     u1 compare_value;
     do {
@@ -344,10 +346,14 @@
   void release_set_intptr_at(int index, intptr_t value);
   intptr_t intptr_at(int index) const {
     assert(0 <= index && index < cell_count(), "oob");
     return data()->cell_at(index);
   }
+  intptr_t* intptr_at_adr(int index) const {
+    assert(0 <= index && index < cell_count(), "oob");
+    return data()->cell_at_adr(index);
+  }
   void set_uint_at(int index, uint value) {
     set_intptr_at(index, (intptr_t) value);
   }
   void release_set_uint_at(int index, uint value);
   uint uint_at(int index) const {
@@ -361,16 +367,10 @@
     return (int)intptr_at(index);
   }
   int int_at_unchecked(int index) const {
     return (int)data()->cell_at(index);
   }
-  void set_oop_at(int index, oop value) {
-    set_intptr_at(index, cast_from_oop<intptr_t>(value));
-  }
-  oop oop_at(int index) const {
-    return cast_to_oop(intptr_at(index));
-  }
 
   void set_flag_at(u1 flag_number) {
     data()->set_flag_at(flag_number);
   }
   bool flag_at(u1 flag_number) const {
@@ -487,11 +487,14 @@
   virtual void post_initialize(BytecodeStream* stream, MethodData* mdo) {}
 
   // GC support
   virtual void clean_weak_klass_links(bool always_clean) {}
 
-  // CI translation: ProfileData can represent both MethodDataOop data
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it) {}
+
+    // CI translation: ProfileData can represent both MethodDataOop data
   // as well as CIMethodData data. This function is provided for translating
   // an oop in a ProfileData to the ci equivalent. Generally speaking,
   // most ProfileData don't require any translation, so we provide the null
   // translation here, and the required translators are in the ci subclasses.
   virtual void translate_from(const ProfileData* data) {}
@@ -852,10 +855,15 @@
   intptr_t type(int i) const {
     assert(i >= 0 && i < _number_of_entries, "oob");
     return _pd->intptr_at(type_offset_in_cells(i));
   }
 
+  intptr_t* type_adr(int i) const {
+    assert(i >= 0 && i < _number_of_entries, "oob");
+    return _pd->intptr_at_adr(type_offset_in_cells(i));
+  }
+
   // set type for entry i
   void set_type(int i, intptr_t k) {
     assert(i >= 0 && i < _number_of_entries, "oob");
     _pd->set_intptr_at(type_offset_in_cells(i), k);
   }
@@ -873,10 +881,13 @@
   }
 
   // GC support
   void clean_weak_klass_links(bool always_clean);
 
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it);
+
   void print_data_on(outputStream* st) const;
 };
 
 // Type entry used for return from a call. A single cell to record the
 // type.
@@ -897,10 +908,14 @@
 
   intptr_t type() const {
     return _pd->intptr_at(_base_off);
   }
 
+  intptr_t* type_adr() const {
+    return _pd->intptr_at_adr(_base_off);
+  }
+
   void set_type(intptr_t k) {
     _pd->set_intptr_at(_base_off, k);
   }
 
   static int static_cell_count() {
@@ -916,10 +931,13 @@
   }
 
   // GC support
   void clean_weak_klass_links(bool always_clean);
 
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it);
+
   void print_data_on(outputStream* st) const;
 };
 
 // Entries to collect type information at a call: contains arguments
 // (TypeStackSlotEntries), a return type (ReturnTypeEntry) and a
@@ -1107,10 +1125,20 @@
     if (has_return()) {
       _ret.clean_weak_klass_links(always_clean);
     }
   }
 
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it) {
+    if (has_arguments()) {
+      _args.metaspace_pointers_do(it);
+    }
+    if (has_return()) {
+      _ret.metaspace_pointers_do(it);
+    }
+  }
+
   virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
 };
 
 // ReceiverTypeData
 //
@@ -1217,10 +1245,13 @@
   }
 
   // GC support
   virtual void clean_weak_klass_links(bool always_clean);
 
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it);
+
   void print_receiver_data_on(outputStream* st) const;
   void print_data_on(outputStream* st, const char* extra = nullptr) const;
 };
 
 // VirtualCallData
@@ -1382,10 +1413,21 @@
     if (has_return()) {
       _ret.clean_weak_klass_links(always_clean);
     }
   }
 
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it) {
+    ReceiverTypeData::metaspace_pointers_do(it);
+    if (has_arguments()) {
+      _args.metaspace_pointers_do(it);
+    }
+    if (has_return()) {
+      _ret.metaspace_pointers_do(it);
+    }
+  }
+
   virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
 };
 
 // RetData
 //
@@ -1565,14 +1607,10 @@
   }
   int array_int_at(int index) const {
     int aindex = index + array_start_off_set;
     return int_at(aindex);
   }
-  oop array_oop_at(int index) const {
-    int aindex = index + array_start_off_set;
-    return oop_at(aindex);
-  }
   void array_set_int_at(int index, int value) {
     int aindex = index + array_start_off_set;
     set_int_at(aindex, value);
   }
 
@@ -1781,10 +1819,15 @@
 
   virtual void clean_weak_klass_links(bool always_clean) {
     _parameters.clean_weak_klass_links(always_clean);
   }
 
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it) {
+    _parameters.metaspace_pointers_do(it);
+  }
+
   virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
 
   static ByteSize stack_slot_offset(int i) {
     return cell_offset(stack_slot_local_offset(i));
   }
@@ -1851,10 +1894,13 @@
 
   static ByteSize method_offset() {
     return cell_offset(speculative_trap_method);
   }
 
+  // CDS support
+  virtual void metaspace_pointers_do(MetaspaceClosure* it);
+
   virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
 };
 
 // MethodData*
 //
@@ -1961,17 +2007,19 @@
   int _size;
 
   // Cached hint for bci_to_dp and bci_to_data
   int _hint_di;
 
-  Mutex _extra_data_lock;
+  Mutex* volatile _extra_data_lock; // FIXME: CDS support
 
   MethodData(const methodHandle& method);
 
   void initialize();
 
 public:
+  MethodData();
+
   static MethodData* allocate(ClassLoaderData* loader_data, const methodHandle& method, TRAPS);
 
   virtual bool is_methodData() const { return true; }
 
   // Safely reinitialize the data in the MDO.  This is intended as a testing facility as the
@@ -1992,18 +2040,19 @@
     friend class JVMCIVMStructs;
 
     uint _nof_decompiles;             // count of all nmethod removals
     uint _nof_overflow_recompiles;    // recompile count, excluding recomp. bits
     uint _nof_overflow_traps;         // trap count, excluding _trap_hist
+    uint __gap;
     union {
       intptr_t _align;
       // JVMCI separates trap history for OSR compilations from normal compilations
       u1 _array[JVMCI_ONLY(2 *) MethodData::_trap_hist_limit];
     } _trap_hist;
 
   public:
-    CompilerCounters() : _nof_decompiles(0), _nof_overflow_recompiles(0), _nof_overflow_traps(0) {
+    CompilerCounters() : _nof_decompiles(0), _nof_overflow_recompiles(0), _nof_overflow_traps(0), __gap(0) {
 #ifndef ZERO
       // Some Zero platforms do not have expected alignment, and do not use
       // this code. static_assert would still fire and fail for them.
       static_assert(sizeof(_trap_hist) % HeapWordSize == 0, "align");
 #endif
@@ -2270,10 +2319,15 @@
   FailedSpeculation** get_failed_speculations_address() {
     return &_failed_speculations;
   }
 #endif
 
+#if INCLUDE_CDS
+  void remove_unshareable_info();
+  void restore_unshareable_info(TRAPS);
+#endif
+
   void set_would_profile(bool p)              { _would_profile = p ? profile : no_profile; }
   bool would_profile() const                  { return _would_profile != no_profile; }
 
   int num_loops() const                       { return _num_loops;  }
   void set_num_loops(short n)                 { _num_loops = n;     }
@@ -2509,10 +2563,10 @@
   static bool profile_parameters();
   static bool profile_return_jsr292_only();
 
   void clean_method_data(bool always_clean);
   void clean_weak_method_links();
-  Mutex* extra_data_lock() const { return const_cast<Mutex*>(&_extra_data_lock); }
+  Mutex* extra_data_lock();
   void check_extra_data_locked() const NOT_DEBUG_RETURN;
 };
 
 #endif // SHARE_OOPS_METHODDATA_HPP
diff a/src/hotspot/share/oops/methodFlags.hpp b/src/hotspot/share/oops/methodFlags.hpp
--- a/src/hotspot/share/oops/methodFlags.hpp
+++ b/src/hotspot/share/oops/methodFlags.hpp
@@ -56,10 +56,13 @@
    status(force_inline                , 1 << 11) /* Annotations but also set/reset at runtime */ \
    status(dont_inline                 , 1 << 12) \
    status(has_loops_flag              , 1 << 13) /* Method has loops */ \
    status(has_loops_flag_init         , 1 << 14) /* The loop flag has been initialized */ \
    status(on_stack_flag               , 1 << 15) /* RedefineClasses support to keep Metadata from being cleaned */ \
+   status(pending_queue_processed     , 1 << 16) \
+   status(has_upcall_on_method_entry  , 1 << 17) \
+   status(has_upcall_on_method_exit   , 1 << 18) \
    /* end of list */
 
 #define M_STATUS_ENUM_NAME(name, value)    _misc_##name = value,
   enum {
     M_STATUS_DO(M_STATUS_ENUM_NAME)
diff a/src/hotspot/share/oops/oop.cpp b/src/hotspot/share/oops/oop.cpp
--- a/src/hotspot/share/oops/oop.cpp
+++ b/src/hotspot/share/oops/oop.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/cds_globals.hpp"
 #include "cds/cdsConfig.hpp"
 #include "classfile/altHashing.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "gc/shared/gc_globals.hpp"
diff a/src/hotspot/share/oops/recompilationSchedule.cpp b/src/hotspot/share/oops/recompilationSchedule.cpp
--- /dev/null
+++ b/src/hotspot/share/oops/recompilationSchedule.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "cds/methodProfiler.hpp"
+#include "code/nmethod.hpp"
+#include "memory/metadataFactory.hpp"
+#include "oops/recompilationSchedule.hpp"
+#include "oops/trainingData.hpp"
+
+Array<MethodTrainingData*>* RecompilationSchedule::_schedule = nullptr;
+Array<MethodTrainingData*>* RecompilationSchedule::_schedule_for_dumping = nullptr;
+volatile bool* RecompilationSchedule::_status = nullptr;
+
+void RecompilationSchedule::initialize() {
+  if (TrainingData::have_data()) {
+    if (_schedule != nullptr && _schedule->length() > 0) {
+      const int size = _schedule->length();
+      _status = NEW_C_HEAP_ARRAY(bool, size, mtCompiler);
+      for (int i = 0; i < size; i++) {
+        _status[i] = false;
+      }
+    }
+  }
+}
+
+void RecompilationSchedule::prepare(TRAPS) {
+  if (!TrainingData::need_data()) {
+    return;
+  }
+#if INCLUDE_CDS
+  auto nmethods = MethodProfiler::sampled_nmethods();
+  GrowableArray<MethodTrainingData*> dyn_schedule;
+  for (auto it = nmethods->begin(); it != nmethods->end(); ++it) {
+    nmethod* nm = *it;
+    if (RecordOnlyTopCompilations && nm->method_profiling_count() == 0) {
+      break;
+    }
+    if (nm->method() != nullptr) {
+      MethodTrainingData* mtd = nm->method()->training_data_or_null();
+      if (mtd != nullptr) {
+        dyn_schedule.append(mtd);
+      }
+    }
+  }
+  delete nmethods;
+  ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
+  _schedule_for_dumping = MetadataFactory::new_array<MethodTrainingData*>(loader_data, dyn_schedule.length(), CHECK);
+  int i = 0;
+  for (auto it = dyn_schedule.begin(); it != dyn_schedule.end(); ++it) {
+    _schedule_for_dumping->at_put(i++, *it);
+  }
+#endif
+}
+
+#if INCLUDE_CDS
+void RecompilationSchedule::iterate_roots(MetaspaceClosure* it) {
+  if (!TrainingData::need_data()) {
+    return;
+  }
+  it->push(&_schedule_for_dumping);
+}
+
+void RecompilationSchedule::cleanup() {
+  _status = nullptr;
+}
+
+void RecompilationSchedule::serialize_training_data(SerializeClosure* soc) {
+  if (soc->writing()) {
+    soc->do_ptr(&_schedule_for_dumping);
+  } else {
+    soc->do_ptr(&_schedule);
+  }
+}
+
+
+void RecompilationSchedule::print_archived_training_data_on(outputStream* st) {
+  if (_schedule != nullptr && _schedule->length() > 0) {
+    st->print_cr("Archived TrainingData Recompilation Schedule");
+    for (int i = 0; i < _schedule->length(); i++) {
+      st->print("%4d: ", i);
+      MethodTrainingData* mtd = _schedule->at(i);
+      if (mtd != nullptr) {
+        mtd->print_on(st);
+      } else {
+        st->print("nullptr");
+      }
+      st->cr();
+    }
+  }
+}
+#endif //INCLUDE_CDS
diff a/src/hotspot/share/oops/recompilationSchedule.hpp b/src/hotspot/share/oops/recompilationSchedule.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/recompilationSchedule.hpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+#ifndef SHARE_OOPS_RECOMPILATION_SCHEDULE_HPP
+#define SHARE_OOPS_RECOMPILATION_SCHEDULE_HPP
+
+#include "cds/serializeClosure.hpp"
+#include "memory/allStatic.hpp"
+#include "memory/metaspaceClosure.hpp"
+#include "oops/array.hpp"
+#include "oops/trainingData.hpp"
+#include "runtime/atomic.hpp"
+#include "utilities/exceptions.hpp"
+#include "utilities/macros.hpp"
+#include "utilities/ostream.hpp"
+
+class RecompilationSchedule : public AllStatic {
+  static Array<MethodTrainingData*>* _schedule;
+  static Array<MethodTrainingData*>* _schedule_for_dumping;
+  static volatile bool* _status;
+public:
+  static void initialize();
+  static void prepare(TRAPS);
+  static bool have_schedule() { return _schedule != nullptr; }
+  static Array<MethodTrainingData*>* schedule() { return _schedule; }
+  static int length() {
+    return have_schedule() ? _schedule->length() : 0;
+  }
+  static MethodTrainingData* at(int i) {
+    assert(i < length(), "");
+    return schedule()->at(i);
+  }
+  static volatile bool* status() { return _status; }
+  static volatile bool* status_adr_at(int i) {
+    assert(i < length(), "");
+    return &_status[i];
+  }
+  static bool status_at(int i) {
+    return Atomic::load_acquire(status_adr_at(i));
+  }
+  static void set_status_at(int i, bool value) {
+    Atomic::release_store(RecompilationSchedule::status_adr_at(i), value);
+  }
+  static bool claim_at(int i) {
+    return Atomic::cmpxchg(RecompilationSchedule::status_adr_at(i), false, true) == false;
+  }
+#if INCLUDE_CDS
+  static void iterate_roots(MetaspaceClosure* it);
+  static void cleanup();
+  static void serialize_training_data(SerializeClosure* soc);
+  static void print_archived_training_data_on(outputStream* st);
+#endif
+};
+
+#endif // SHARE_OOPS_RECOMPILATION_SCHEDULE_HPP
diff a/src/hotspot/share/oops/resolvedMethodEntry.cpp b/src/hotspot/share/oops/resolvedMethodEntry.cpp
--- a/src/hotspot/share/oops/resolvedMethodEntry.cpp
+++ b/src/hotspot/share/oops/resolvedMethodEntry.cpp
@@ -63,10 +63,85 @@
     ArchiveBuilder::current()->mark_and_relocate_to_buffered_addr(&_method);
   }
   if (bytecode1() == Bytecodes::_invokeinterface) {
     ArchiveBuilder::current()->mark_and_relocate_to_buffered_addr(&_entry_specific._interface_klass);
   }
+#if 0
+  // OLD CODE ... some of it may need to be salvaged.
+  Bytecodes::Code invoke_code = bytecode_1();
+  if (invoke_code != (Bytecodes::Code)0) {
+    Metadata* f1 = f1_ord();
+    if (f1 != nullptr) {
+      ArchiveBuilder::current()->mark_and_relocate_to_buffered_addr(&_f1);
+      switch (invoke_code) {
+      case Bytecodes::_invokeinterface:
+        assert(0, "not implemented");
+        //assert(f1->is_klass(), "");
+        //ArchiveBuilder::current()->mark_and_relocate_to_buffered_addr(&_f2); // f2 is interface method
+        return false;
+      case Bytecodes::_invokestatic:
+        // For safety, we support invokestatic only for invoking methods in MethodHandle.
+        // FIXME -- further restrict it to linkToStatic(), etc?
+        assert(bytecode_2() == (Bytecodes::Code)0, "must be");
+        assert(f1->is_method(), "");
+        assert(f1_as_method()->method_holder()->name()->equals("java/lang/invoke/MethodHandle") ||
+               f1_as_method()->method_holder()->name()->equals("java/lang/invoke/MethodHandleNatives"), "sanity");
+        return true;
+      case Bytecodes::_invokespecial:
+        assert(f1->is_method(), "must be");
+        // Also need to work on bytecode_2() below.
+        break;
+      case Bytecodes::_invokehandle:
+        assert(bytecode_2() == (Bytecodes::Code)0, "must be");
+        assert(f1->is_method(), "");
+        return true;
+      default:
+        ShouldNotReachHere();
+        break;
+      }
+    }
+  }
+
+  // TODO test case: can invokespecial and invokevirtual share the same CP?
+  invoke_code = bytecode_2();
+  if (invoke_code != (Bytecodes::Code)0) {
+    assert(invoke_code == Bytecodes::_invokevirtual, "must be");
+    if (is_vfinal()) {
+      // f2 is vfinal method
+      ArchiveBuilder::current()->mark_and_relocate_to_buffered_addr(&_f2); // f2 is final method
+    } else {
+      // f2 is vtable index, no need to mark
+      if (DynamicDumpSharedSpaces) {
+        // InstanceKlass::methods() is has been resorted, so we need to
+        // update the vtable_index.
+        int holder_index = src_cp->uncached_klass_ref_index_at(constant_pool_index());
+        Klass* src_klass = src_cp->resolved_klass_at(holder_index);
+        Method* src_m = src_klass->method_at_vtable(f2_as_index());
+        if (!ArchiveBuilder::current()->is_in_mapped_static_archive(src_m->method_holder()) &&
+            !ArchiveBuilder::current()->is_in_mapped_static_archive(src_m)) {
+          Klass* buffered_klass = ArchiveBuilder::current()->get_buffered_addr(src_klass);
+          Method* buffered_m = ArchiveBuilder::current()->get_buffered_addr(src_m);
+          int vtable_index;
+          if (src_m->method_holder()->is_interface()) { // default or miranda method
+            assert(src_m->vtable_index() < 0, "must be");
+            assert(buffered_klass->is_instance_klass(), "must be");
+            vtable_index = InstanceKlass::cast(buffered_klass)->vtable_index_of_interface_method(buffered_m);
+            assert(vtable_index >= 0, "must be");
+          } else {
+            vtable_index = buffered_m->vtable_index();
+            assert(vtable_index >= 0, "must be");
+          }
+          if (_f2 != vtable_index) {
+            log_trace(cds, resolve)("vtable_index changed %d => %d", (int)_f2, vtable_index);
+            _f2 = vtable_index;
+          }
+        }
+      }
+    }
+  }
+
+#endif
 }
 #endif
 
 void ResolvedMethodEntry::print_on(outputStream* st) const {
   st->print_cr("Method Entry:");
diff a/src/hotspot/share/oops/symbol.hpp b/src/hotspot/share/oops/symbol.hpp
--- a/src/hotspot/share/oops/symbol.hpp
+++ b/src/hotspot/share/oops/symbol.hpp
@@ -156,10 +156,13 @@
   unsigned identity_hash() const {
     unsigned addr_bits = (unsigned)((uintptr_t)this >> LogBytesPerWord);
     return ((unsigned)extract_hash(_hash_and_refcount) & 0xffff) |
            ((addr_bits ^ (length() << 8) ^ (( _body[0] << 8) | _body[1])) << 16);
   }
+  static unsigned identity_hash(const Symbol* symbol_or_null) {
+    return symbol_or_null == nullptr ? 0 : symbol_or_null->identity_hash();
+  }
 
   // Reference counting.  See comments above this class for when to use.
   int refcount() const { return extract_refcount(_hash_and_refcount); }
   bool try_increment_refcount();
   void increment_refcount();
@@ -245,10 +248,23 @@
 
   // Three-way compare for sorting; returns -1/0/1 if receiver is </==/> than arg
   // note that the ordering is not alfabetical
   inline int fast_compare(const Symbol* other) const;
 
+  // Perform a memcmp against the other block of bytes, up to the end
+  // of the shorter of the two.  If lengths differ but the bytes are
+  // the same, the shorter one compares lower.
+  int cmp(const Symbol* other) const {
+    return cmp((char*)other->base(), other->utf8_length());
+  }
+  int cmp(const char* str, int len) const {
+    int mylen = utf8_length();
+    int cmp = memcmp((char*)base(), str, mylen < len ? mylen : len);
+    // mylen - len cannot overflow because symbol length >= 0
+    return cmp != 0 ? cmp : mylen - len;
+  }
+
   // Returns receiver converted to null-terminated UTF-8 string; string is
   // allocated in resource area, or in the char buffer provided by caller.
   char* as_C_string() const;
   char* as_C_string(char* buf, int size) const;
 
diff a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp
--- /dev/null
+++ b/src/hotspot/share/oops/trainingData.cpp
@@ -0,0 +1,851 @@
+/*
+ * Copyright (c) 2023, 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "ci/ciEnv.hpp"
+#include "ci/ciMetadata.hpp"
+#include "cds/cdsConfig.hpp"
+#include "cds/metaspaceShared.hpp"
+#include "classfile/classLoaderData.hpp"
+#include "classfile/compactHashtable.hpp"
+#include "classfile/javaClasses.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "compiler/compileTask.hpp"
+#include "memory/metadataFactory.hpp"
+#include "memory/metaspaceClosure.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/method.hpp"
+#include "oops/methodCounters.hpp"
+#include "oops/recompilationSchedule.hpp"
+#include "oops/trainingData.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/javaThread.inline.hpp"
+#include "runtime/jniHandles.inline.hpp"
+#include "utilities/growableArray.hpp"
+
+TrainingData::TrainingDataSet TrainingData::_training_data_set(1024, 0x3fffffff);
+TrainingData::TrainingDataDictionary TrainingData::_archived_training_data_dictionary;
+TrainingData::TrainingDataDictionary TrainingData::_archived_training_data_dictionary_for_dumping;
+TrainingData::DumptimeTrainingDataDictionary* TrainingData::_dumptime_training_data_dictionary = nullptr;
+int TrainingData::TrainingDataLocker::_lock_mode;
+volatile bool TrainingData::TrainingDataLocker::_snapshot = false;
+
+MethodTrainingData::MethodTrainingData() {
+  assert(CDSConfig::is_dumping_static_archive() || UseSharedSpaces, "only for CDS");
+}
+
+KlassTrainingData::KlassTrainingData() {
+  assert(CDSConfig::is_dumping_static_archive() || UseSharedSpaces, "only for CDS");
+}
+
+CompileTrainingData::CompileTrainingData() : _level(-1), _compile_id(-1) {
+  assert(CDSConfig::is_dumping_static_archive() || UseSharedSpaces, "only for CDS");
+}
+
+void TrainingData::initialize() {
+  // this is a nop if training modes are not enabled
+  if (have_data() || need_data()) {
+    TrainingDataLocker::initialize();
+  }
+  RecompilationSchedule::initialize();
+}
+
+#if INCLUDE_CDS
+static void verify_archived_entry(TrainingData* td, const TrainingData::Key* k) {
+  guarantee(TrainingData::Key::can_compute_cds_hash(k), "");
+  TrainingData* td1 = TrainingData::lookup_archived_training_data(k);
+  guarantee(td == td1, "");
+}
+#endif
+
+void TrainingData::verify() {
+#if INCLUDE_CDS
+  if (TrainingData::have_data()) {
+    archived_training_data_dictionary()->iterate([&](TrainingData* td) {
+      if (td->is_KlassTrainingData()) {
+        KlassTrainingData* ktd = td->as_KlassTrainingData();
+        if (ktd->has_holder() && ktd->holder()->is_loaded()) {
+          Key k(ktd->holder());
+          verify_archived_entry(td, &k);
+        }
+        ktd->verify();
+      } else if (td->is_MethodTrainingData()) {
+        MethodTrainingData* mtd = td->as_MethodTrainingData();
+        if (mtd->has_holder() && mtd->holder()->method_holder()->is_loaded()) {
+          Key k(mtd->holder());
+          verify_archived_entry(td, &k);
+        }
+        mtd->verify();
+      } else if (td->is_CompileTrainingData()) {
+        td->as_CompileTrainingData()->verify();
+      }
+    });
+  }
+#endif
+}
+
+MethodTrainingData* MethodTrainingData::make(const methodHandle& method,
+                                             bool null_if_not_found) {
+  MethodTrainingData* mtd = nullptr;
+  if (!have_data() && !need_data()) {
+    return mtd;
+  }
+  // Try grabbing the cached value first.
+  MethodCounters* mcs = method->method_counters();
+  if (mcs != nullptr) {
+    mtd = mcs->method_training_data();
+    if (mtd != nullptr) {
+      return mtd;
+    }
+  } else {
+    mcs = Method::build_method_counters(Thread::current(), method());
+  }
+
+  KlassTrainingData* holder = KlassTrainingData::make(method->method_holder(), null_if_not_found);
+  if (holder == nullptr) {
+    return nullptr; // allocation failure
+  }
+  Key key(method());
+  TrainingData* td = CDS_ONLY(have_data() ? lookup_archived_training_data(&key) :) nullptr;
+  if (td != nullptr) {
+    mtd = td->as_MethodTrainingData();
+    method->init_training_data(mtd);  // Cache the pointer for next time.
+    return mtd;
+  } else {
+    TrainingDataLocker l;
+    td = training_data_set()->find(&key);
+    if (td == nullptr && null_if_not_found) {
+      return nullptr;
+    }
+    if (td != nullptr) {
+      mtd = td->as_MethodTrainingData();
+      method->init_training_data(mtd); // Cache the pointer for next time.
+      return mtd;
+    }
+  }
+  assert(td == nullptr && mtd == nullptr && !null_if_not_found, "Should return if have result");
+  KlassTrainingData* ktd = KlassTrainingData::make(method->method_holder());
+  if (ktd != nullptr) {
+    TrainingDataLocker l;
+    td = training_data_set()->find(&key);
+    if (td == nullptr) {
+      mtd = MethodTrainingData::allocate(method(), ktd);
+      if (mtd == nullptr) {
+        return nullptr; // allocation failure
+      }
+      td = training_data_set()->install(mtd);
+      assert(td == mtd, "");
+    } else {
+      mtd = td->as_MethodTrainingData();
+    }
+    method->init_training_data(mtd);
+  }
+  return mtd;
+}
+
+void MethodTrainingData::print_on(outputStream* st, bool name_only) const {
+  if (has_holder()) {
+    _klass->print_on(st, true);
+    st->print(".");
+    name()->print_symbol_on(st);
+    signature()->print_symbol_on(st);
+  }
+  if (name_only) {
+    return;
+  }
+  if (!has_holder()) {
+    st->print("[SYM]");
+  }
+  if (_level_mask) {
+    st->print(" LM%d", _level_mask);
+  }
+  st->print(" mc=%p mdo=%p", _final_counters, _final_profile);
+}
+
+CompileTrainingData* CompileTrainingData::make(CompileTask* task) {
+  int level = task->comp_level();
+  int compile_id = task->compile_id();
+  Thread* thread = Thread::current();
+  methodHandle m(thread, task->method());
+  MethodTrainingData* mtd = MethodTrainingData::make(m);
+  if (mtd == nullptr) {
+    return nullptr; // allocation failure
+  }
+  mtd->notice_compilation(level);
+
+  TrainingDataLocker l;
+  CompileTrainingData* ctd = CompileTrainingData::allocate(mtd, level, compile_id);
+  if (ctd != nullptr) {
+    if (mtd->_last_toplevel_compiles[level - 1] != nullptr) {
+      if (mtd->_last_toplevel_compiles[level - 1]->compile_id() < compile_id) {
+        mtd->_last_toplevel_compiles[level - 1]->clear_init_deps();
+        mtd->_last_toplevel_compiles[level - 1] = ctd;
+        mtd->_highest_top_level = MAX2(mtd->_highest_top_level, level);
+      }
+    } else {
+      mtd->_last_toplevel_compiles[level - 1] = ctd;
+      mtd->_highest_top_level = MAX2(mtd->_highest_top_level, level);
+    }
+  }
+  return ctd;
+}
+
+
+void CompileTrainingData::dec_init_deps_left(KlassTrainingData* ktd) {
+  LogStreamHandle(Trace, training) log;
+  if (log.is_enabled()) {
+    log.print("CTD "); print_on(&log); log.cr();
+    log.print("KTD "); ktd->print_on(&log); log.cr();
+  }
+  assert(ktd!= nullptr && ktd->has_holder(), "");
+  assert(_init_deps.contains(ktd), "");
+  assert(_init_deps_left > 0, "");
+
+  uint init_deps_left1 = Atomic::sub(&_init_deps_left, 1);
+
+  if (log.is_enabled()) {
+    uint init_deps_left2 = compute_init_deps_left();
+    log.print("init_deps_left: %d (%d)", init_deps_left1, init_deps_left2);
+    ktd->print_on(&log, true);
+  }
+}
+
+uint CompileTrainingData::compute_init_deps_left(bool count_initialized) {
+  int left = 0;
+  for (int i = 0; i < _init_deps.length(); i++) {
+    KlassTrainingData* ktd = _init_deps.at(i);
+    // Ignore symbolic refs and already initialized classes (unless explicitly requested).
+    if (ktd->has_holder()) {
+      InstanceKlass* holder = ktd->holder();
+      if (!ktd->holder()->is_initialized() || count_initialized) {
+        ++left;
+      } else if (holder->is_shared_unregistered_class()) {
+        Key k(holder);
+        if (CDS_ONLY(!Key::can_compute_cds_hash(&k)) NOT_CDS(true)) {
+          ++left; // FIXME: !!! init tracking doesn't work well for custom loaders !!!
+        }
+      }
+    }
+  }
+  return left;
+}
+
+void CompileTrainingData::print_on(outputStream* st, bool name_only) const {
+  _method->print_on(st, true);
+  st->print("#%dL%d", _compile_id, _level);
+  if (name_only) {
+    return;
+  }
+  if (_init_deps.length() > 0) {
+    if (_init_deps_left > 0) {
+      st->print(" udeps=%d", _init_deps_left);
+    }
+    for (int i = 0, len = _init_deps.length(); i < len; i++) {
+      st->print(" dep:");
+      _init_deps.at(i)->print_on(st, true);
+    }
+  }
+}
+
+void CompileTrainingData::notice_inlined_method(CompileTask* task,
+                                                const methodHandle& method) {
+  MethodTrainingData* mtd = MethodTrainingData::make(method);
+  if (mtd != nullptr) {
+    mtd->notice_compilation(task->comp_level(), true);
+  }
+}
+
+void CompileTrainingData::notice_jit_observation(ciEnv* env, ciBaseObject* what) {
+  // A JIT is starting to look at class k.
+  // We could follow the queries that it is making, but it is
+  // simpler to assume, conservatively, that the JIT will
+  // eventually depend on the initialization state of k.
+  CompileTask* task = env->task();
+  assert(task != nullptr, "");
+  Method* method = task->method();
+  InstanceKlass* compiling_klass = method->method_holder();
+  if (what->is_metadata()) {
+    ciMetadata* md = what->as_metadata();
+    if (md->is_loaded() && md->is_instance_klass()) {
+      ciInstanceKlass* cik = md->as_instance_klass();
+
+      if (cik->is_initialized()) {
+        InstanceKlass* ik = md->as_instance_klass()->get_instanceKlass();
+        KlassTrainingData* ktd = KlassTrainingData::make(ik);
+        if (ktd == nullptr) {
+          // Allocation failure or snapshot in progress
+          return;
+        }
+        // This JIT task is (probably) requesting that ik be initialized,
+        // so add him to my _init_deps list.
+        TrainingDataLocker l;
+        add_init_dep(ktd);
+      }
+    }
+  }
+}
+
+void KlassTrainingData::prepare(Visitor& visitor) {
+  if (visitor.is_visited(this)) {
+    return;
+  }
+  visitor.visit(this);
+  ClassLoaderData* loader_data = nullptr;
+  if (_holder != nullptr) {
+    loader_data = _holder->class_loader_data();
+  } else {
+    loader_data = java_lang_ClassLoader::loader_data(SystemDictionary::java_system_loader()); // default CLD
+  }
+  _comp_deps.prepare(loader_data);
+}
+
+void MethodTrainingData::prepare(Visitor& visitor) {
+  if (visitor.is_visited(this)) {
+    return;
+  }
+  visitor.visit(this);
+  klass()->prepare(visitor);
+  if (has_holder()) {
+    _final_counters = holder()->method_counters();
+    _final_profile  = holder()->method_data();
+    assert(_final_profile == nullptr || _final_profile->method() == holder(), "");
+  }
+  for (int i = 0; i < CompLevel_count; i++) {
+    CompileTrainingData* ctd = _last_toplevel_compiles[i];
+    if (ctd != nullptr) {
+      ctd->prepare(visitor);
+    }
+  }
+}
+
+void CompileTrainingData::prepare(Visitor& visitor) {
+  if (visitor.is_visited(this)) {
+    return;
+  }
+  visitor.visit(this);
+  method()->prepare(visitor);
+  ClassLoaderData* loader_data = _method->klass()->class_loader_data();
+  _init_deps.prepare(loader_data);
+  _ci_records.prepare(loader_data);
+}
+
+KlassTrainingData* KlassTrainingData::make(InstanceKlass* holder, bool null_if_not_found) {
+  Key key(holder);
+  TrainingData* td = CDS_ONLY(have_data() ? lookup_archived_training_data(&key) :) nullptr;
+  KlassTrainingData* ktd = nullptr;
+  if (td != nullptr) {
+    ktd = td->as_KlassTrainingData();
+    guarantee(!ktd->has_holder() || ktd->holder() == holder, "");
+    if (ktd->has_holder()) {
+      return ktd;
+    }
+  }
+  TrainingDataLocker l;
+  td = training_data_set()->find(&key);
+  if (td == nullptr) {
+    if (null_if_not_found) {
+      return nullptr;
+    }
+    ktd = KlassTrainingData::allocate(holder);
+    if (ktd == nullptr) {
+      return nullptr; // allocation failure
+    }
+    td = training_data_set()->install(ktd);
+    assert(ktd == td, "");
+  } else {
+    ktd = td->as_KlassTrainingData();
+    guarantee(ktd->holder() != nullptr, "null holder");
+  }
+  assert(ktd != nullptr, "");
+  guarantee(ktd->holder() == holder, "");
+  return ktd;
+}
+
+void KlassTrainingData::print_on(outputStream* st, bool name_only) const {
+  if (has_holder()) {
+    name()->print_symbol_on(st);
+    switch (holder()->init_state()) {
+      case InstanceKlass::allocated:            st->print("[A]"); break;
+      case InstanceKlass::loaded:               st->print("[D]"); break;
+      case InstanceKlass::linked:               st->print("[L]"); break;
+      case InstanceKlass::being_initialized:    st->print("[i]"); break;
+      case InstanceKlass::fully_initialized:    /*st->print("");*/ break;
+      case InstanceKlass::initialization_error: st->print("[E]"); break;
+      default: fatal("unknown state: %d", holder()->init_state());
+    }
+    if (holder()->is_interface()) {
+      st->print("I");
+    }
+  } else {
+    st->print("[SYM]");
+  }
+  if (name_only) {
+    return;
+  }
+  if (_comp_deps.length() > 0) {
+    for (int i = 0, len = _comp_deps.length(); i < len; i++) {
+      st->print(" dep:");
+      _comp_deps.at(i)->print_on(st, true);
+    }
+  }
+}
+
+KlassTrainingData::KlassTrainingData(InstanceKlass* klass) : TrainingData(klass) {
+  if (holder() == klass) {
+    return;   // no change to make
+  }
+
+  jobject hmj = _holder_mirror;
+  if (hmj != nullptr) {   // clear out previous handle, if any
+    _holder_mirror = nullptr;
+    assert(JNIHandles::is_global_handle(hmj), "");
+    JNIHandles::destroy_global(hmj);
+  }
+
+  // Keep the klass alive during the training run, unconditionally.
+  //
+  // FIXME: Revisit this decision; we could allow training runs to
+  // unload classes in the normal way.  We might use make_weak_global
+  // instead of make_global.
+  //
+  // The data from the training run would mention the name of the
+  // unloaded class (and of its loader).  Is it worth the complexity
+  // to track and then unload classes, remembering just their names?
+
+  if (klass != nullptr) {
+    Handle hm(JavaThread::current(), klass->java_mirror());
+    hmj = JNIHandles::make_global(hm);
+    Atomic::release_store(&_holder_mirror, hmj);
+  }
+
+  Atomic::release_store(&_holder, const_cast<InstanceKlass*>(klass));
+  assert(holder() == klass, "");
+}
+
+void KlassTrainingData::notice_fully_initialized() {
+  ResourceMark rm;
+  assert(has_holder(), "");
+  assert(holder()->is_initialized(), "wrong state: %s %s",
+         holder()->name()->as_C_string(), holder()->init_state_name());
+
+  TrainingDataLocker l; // Not a real lock if we don't collect the data,
+                        // that's why we need the atomic decrement below.
+  for (int i = 0; i < comp_dep_count(); i++) {
+    comp_dep(i)->dec_init_deps_left(this);
+  }
+  holder()->set_has_init_deps_processed();
+}
+
+void TrainingData::init_dumptime_table(TRAPS) {
+  if (!need_data()) {
+    return;
+  }
+  _dumptime_training_data_dictionary = new DumptimeTrainingDataDictionary();
+  if (CDSConfig::is_dumping_final_static_archive()) {
+    _archived_training_data_dictionary.iterate([&](TrainingData* record) {
+      _dumptime_training_data_dictionary->append(record);
+    });
+  } else {
+    TrainingDataLocker l;
+    TrainingDataLocker::snapshot();
+
+    ResourceMark rm;
+    Visitor visitor(training_data_set()->size());
+    training_data_set()->iterate_all([&](const TrainingData::Key* k, TrainingData* td) {
+      td->prepare(visitor);
+      if (!td->is_CompileTrainingData()) {
+        _dumptime_training_data_dictionary->append(td);
+      }
+    });
+
+    if (VerifyTrainingData) {
+      training_data_set()->verify();
+    }
+  }
+
+  RecompilationSchedule::prepare(CHECK);
+}
+
+#if INCLUDE_CDS
+void TrainingData::iterate_roots(MetaspaceClosure* it) {
+  if (!need_data()) {
+    return;
+  }
+  assert(_dumptime_training_data_dictionary != nullptr, "");
+  for (int i = 0; i < _dumptime_training_data_dictionary->length(); i++) {
+    _dumptime_training_data_dictionary->at(i).metaspace_pointers_do(it);
+  }
+  RecompilationSchedule::iterate_roots(it);
+}
+
+void TrainingData::dump_training_data() {
+  if (!need_data()) {
+    return;
+  }
+  write_training_data_dictionary(&_archived_training_data_dictionary_for_dumping);
+}
+
+void TrainingData::cleanup_training_data() {
+  if (_dumptime_training_data_dictionary != nullptr) {
+    ResourceMark rm;
+    Visitor visitor(_dumptime_training_data_dictionary->length());
+    for (int i = 0; i < _dumptime_training_data_dictionary->length(); i++) {
+      TrainingData* td = _dumptime_training_data_dictionary->at(i).training_data();
+      td->cleanup(visitor);
+    }
+    // Throw away all elements with empty keys
+    int j = 0;
+    for (int i = 0; i < _dumptime_training_data_dictionary->length(); i++) {
+      TrainingData* td = _dumptime_training_data_dictionary->at(i).training_data();
+      if (td->key()->is_empty()) {
+        continue;
+      }
+      if (i != j) { // no need to copy if it's the same
+        _dumptime_training_data_dictionary->at_put(j, td);
+      }
+      j++;
+    }
+    _dumptime_training_data_dictionary->trunc_to(j);
+  }
+  RecompilationSchedule::cleanup();
+}
+
+void KlassTrainingData::cleanup(Visitor& visitor) {
+  if (visitor.is_visited(this)) {
+    return;
+  }
+  visitor.visit(this);
+  if (has_holder()) {
+    bool is_excluded = !holder()->is_loaded() || SystemDictionaryShared::check_for_exclusion(holder(), nullptr);
+    if (is_excluded) {
+      ResourceMark rm;
+      log_debug(cds)("Cleanup KTD %s", name()->as_klass_external_name());
+      _holder = nullptr;
+      key()->make_empty();
+    }
+  }
+  for (int i = 0; i < _comp_deps.length(); i++) {
+    _comp_deps.at(i)->cleanup(visitor);
+  }
+}
+
+void MethodTrainingData::cleanup(Visitor& visitor) {
+  if (visitor.is_visited(this)) {
+    return;
+  }
+  visitor.visit(this);
+  if (has_holder()) {
+    if (SystemDictionaryShared::check_for_exclusion(holder()->method_holder(), nullptr)) {
+      log_debug(cds)("Cleanup MTD %s::%s", name()->as_klass_external_name(), signature()->as_utf8());
+      if (_final_profile != nullptr && _final_profile->method() != _holder) {
+        log_warning(cds)("Stale MDO for  %s::%s", name()->as_klass_external_name(), signature()->as_utf8());
+      }
+      _holder = nullptr;
+      key()->make_empty();
+    }
+  }
+  for (int i = 0; i < CompLevel_count; i++) {
+    CompileTrainingData* ctd = _last_toplevel_compiles[i];
+    if (ctd != nullptr) {
+      ctd->cleanup(visitor);
+    }
+  }
+}
+
+void KlassTrainingData::verify() {
+  for (int i = 0; i < comp_dep_count(); i++) {
+    CompileTrainingData* ctd = comp_dep(i);
+    if (!ctd->_init_deps.contains(this)) {
+      print_on(tty); tty->cr();
+      ctd->print_on(tty); tty->cr();
+    }
+    guarantee(ctd->_init_deps.contains(this), "");
+  }
+}
+
+void MethodTrainingData::verify() {
+  iterate_all_compiles([](CompileTrainingData* ctd) {
+    ctd->verify();
+
+    int init_deps_left1 = ctd->init_deps_left();
+    int init_deps_left2 = ctd->compute_init_deps_left();
+
+    if (init_deps_left1 != init_deps_left2) {
+      ctd->print_on(tty); tty->cr();
+    }
+    guarantee(init_deps_left1 == init_deps_left2, "mismatch: %d %d %d",
+              init_deps_left1, init_deps_left2, ctd->init_deps_left());
+  });
+}
+
+void CompileTrainingData::verify() {
+  for (int i = 0; i < init_dep_count(); i++) {
+    KlassTrainingData* ktd = init_dep(i);
+    if (ktd->has_holder() && ktd->holder()->is_shared_unregistered_class()) {
+      LogStreamHandle(Warning, training) log;
+      if (log.is_enabled()) {
+        ResourceMark rm;
+        log.print("CTD "); print_value_on(&log);
+        log.print(" depends on unregistered class %s", ktd->holder()->name()->as_C_string());
+      }
+    }
+    if (!ktd->_comp_deps.contains(this)) {
+      print_on(tty); tty->cr();
+      ktd->print_on(tty); tty->cr();
+    }
+    guarantee(ktd->_comp_deps.contains(this), "");
+  }
+}
+
+void CompileTrainingData::cleanup(Visitor& visitor) {
+  if (visitor.is_visited(this)) {
+    return;
+  }
+  visitor.visit(this);
+  method()->cleanup(visitor);
+}
+
+void TrainingData::serialize_training_data(SerializeClosure* soc) {
+  if (soc->writing()) {
+    _archived_training_data_dictionary_for_dumping.serialize_header(soc);
+  } else {
+    _archived_training_data_dictionary.serialize_header(soc);
+  }
+  RecompilationSchedule::serialize_training_data(soc);
+}
+
+void TrainingData::print_archived_training_data_on(outputStream* st) {
+  st->print_cr("Archived TrainingData Dictionary");
+  TrainingDataPrinter tdp(st);
+  TrainingDataLocker::initialize();
+  _archived_training_data_dictionary.iterate(&tdp);
+  RecompilationSchedule::print_archived_training_data_on(st);
+}
+
+void TrainingData::Key::metaspace_pointers_do(MetaspaceClosure *iter) {
+  iter->push(const_cast<Metadata**>(&_meta));
+}
+
+void TrainingData::metaspace_pointers_do(MetaspaceClosure* iter) {
+  _key.metaspace_pointers_do(iter);
+}
+
+bool TrainingData::Key::can_compute_cds_hash(const Key* const& k) {
+  return k->meta() == nullptr || MetaspaceObj::is_shared(k->meta());
+}
+
+uint TrainingData::Key::cds_hash(const Key* const& k) {
+  return SystemDictionaryShared::hash_for_shared_dictionary((address)k->meta());
+}
+
+void TrainingData::write_training_data_dictionary(TrainingDataDictionary* dictionary) {
+  if (!need_data()) {
+    return;
+  }
+  assert(_dumptime_training_data_dictionary != nullptr, "");
+  CompactHashtableStats stats;
+  dictionary->reset();
+  CompactHashtableWriter writer(_dumptime_training_data_dictionary->length(), &stats);
+  for (int i = 0; i < _dumptime_training_data_dictionary->length(); i++) {
+    TrainingData* td = _dumptime_training_data_dictionary->at(i).training_data();
+#ifdef ASSERT
+    for (int j = i+1; j < _dumptime_training_data_dictionary->length(); j++) {
+      TrainingData* td1 = _dumptime_training_data_dictionary->at(j).training_data();
+      assert(!TrainingData::Key::equals(td1, td->key(), -1), "conflict");
+    }
+#endif // ASSERT
+    td = ArchiveBuilder::current()->get_buffered_addr(td);
+    uint hash = TrainingData::Key::cds_hash(td->key());
+    u4 delta = ArchiveBuilder::current()->buffer_to_offset_u4((address)td);
+    writer.add(hash, delta);
+  }
+  writer.dump(dictionary, "training data dictionary");
+}
+
+size_t TrainingData::estimate_size_for_archive() {
+  if (_dumptime_training_data_dictionary != nullptr) {
+    return CompactHashtableWriter::estimate_size(_dumptime_training_data_dictionary->length());
+  } else {
+    return 0;
+  }
+}
+
+TrainingData* TrainingData::lookup_archived_training_data(const Key* k) {
+  // For this to work, all components of the key must be in shared metaspace.
+  if (!TrainingData::Key::can_compute_cds_hash(k) || _archived_training_data_dictionary.empty()) {
+    return nullptr;
+  }
+  uint hash = TrainingData::Key::cds_hash(k);
+  TrainingData* td = _archived_training_data_dictionary.lookup(k, hash, -1 /*unused*/);
+  if (td != nullptr) {
+    if ((td->is_KlassTrainingData()  && td->as_KlassTrainingData()->has_holder()) ||
+        (td->is_MethodTrainingData() && td->as_MethodTrainingData()->has_holder())) {
+      return td;
+    } else {
+      ShouldNotReachHere();
+    }
+  }
+  return nullptr;
+}
+#endif
+
+KlassTrainingData* TrainingData::lookup_for(InstanceKlass* ik) {
+#if INCLUDE_CDS
+  if (TrainingData::have_data() && ik != nullptr && ik->is_loaded()) {
+    TrainingData::Key key(ik);
+    TrainingData* td = TrainingData::lookup_archived_training_data(&key);
+    if (td != nullptr && td->is_KlassTrainingData()) {
+      return td->as_KlassTrainingData();
+    }
+  }
+#endif
+  return nullptr;
+}
+
+MethodTrainingData* TrainingData::lookup_for(Method* m) {
+#if INCLUDE_CDS
+  if (TrainingData::have_data() && m != nullptr) {
+    KlassTrainingData* holder_ktd = TrainingData::lookup_for(m->method_holder());
+    if (holder_ktd != nullptr) {
+      TrainingData::Key key(m);
+      TrainingData* td = TrainingData::lookup_archived_training_data(&key);
+      if (td != nullptr && td->is_MethodTrainingData()) {
+        return td->as_MethodTrainingData();
+      }
+    }
+  }
+#endif
+  return nullptr;
+}
+
+template <typename T>
+void TrainingData::DepList<T>::metaspace_pointers_do(MetaspaceClosure* iter) {
+  iter->push(&_deps);
+}
+
+void KlassTrainingData::metaspace_pointers_do(MetaspaceClosure* iter) {
+  log_trace(cds)("Iter(KlassTrainingData): %p", this);
+#if INCLUDE_CDS
+  TrainingData::metaspace_pointers_do(iter);
+#endif
+  _comp_deps.metaspace_pointers_do(iter);
+  iter->push(&_holder);
+}
+
+void MethodTrainingData::metaspace_pointers_do(MetaspaceClosure* iter) {
+  log_trace(cds)("Iter(MethodTrainingData): %p", this);
+#if INCLUDE_CDS
+  TrainingData::metaspace_pointers_do(iter);
+#endif
+  iter->push(&_klass);
+  iter->push((Method**)&_holder);
+  for (int i = 0; i < CompLevel_count; i++) {
+    iter->push(&_last_toplevel_compiles[i]);
+  }
+  iter->push(&_final_profile);
+  iter->push(&_final_counters);
+}
+
+void CompileTrainingData::metaspace_pointers_do(MetaspaceClosure* iter) {
+  log_trace(cds)("Iter(CompileTrainingData): %p", this);
+#if INCLUDE_CDS
+  TrainingData::metaspace_pointers_do(iter);
+#endif
+  _init_deps.metaspace_pointers_do(iter);
+  _ci_records.metaspace_pointers_do(iter);
+  iter->push(&_method);
+}
+
+template <typename T>
+void TrainingData::DepList<T>::prepare(ClassLoaderData* loader_data) {
+  if (_deps == nullptr && _deps_dyn != nullptr) {
+    int len = _deps_dyn->length();
+    _deps = MetadataFactory::new_array_from_c_heap<T>(len, mtClassShared);
+    for (int i = 0; i < len; i++) {
+      _deps->at_put(i, _deps_dyn->at(i)); // copy
+    }
+  }
+}
+
+void TrainingDataPrinter::do_value(TrainingData* td) {
+#ifdef ASSERT
+#if INCLUDE_CDS
+  TrainingData::Key key(td->key()->meta());
+  assert(td == TrainingData::archived_training_data_dictionary()->lookup(td->key(), TrainingData::Key::cds_hash(td->key()), -1), "");
+  assert(td == TrainingData::archived_training_data_dictionary()->lookup(&key, TrainingData::Key::cds_hash(&key), -1), "");
+#endif
+#endif // ASSERT
+
+  const char* type = (td->is_KlassTrainingData()   ? "K" :
+                      td->is_MethodTrainingData()  ? "M" :
+                      td->is_CompileTrainingData() ? "C" : "?");
+  _st->print("%4d: %p %s ", _index++, td, type);
+  td->print_on(_st);
+  _st->cr();
+  if (td->is_KlassTrainingData()) {
+    td->as_KlassTrainingData()->iterate_all_comp_deps([&](CompileTrainingData* ctd) {
+      ResourceMark rm;
+      _st->print_raw("  C ");
+      ctd->print_on(_st);
+      _st->cr();
+    });
+  } else if (td->is_MethodTrainingData()) {
+    td->as_MethodTrainingData()->iterate_all_compiles([&](CompileTrainingData* ctd) {
+      ResourceMark rm;
+      _st->print_raw("  C ");
+      ctd->print_on(_st);
+      _st->cr();
+    });
+  } else if (td->is_CompileTrainingData()) {
+    // ?
+  }
+}
+
+
+#if INCLUDE_CDS
+void KlassTrainingData::remove_unshareable_info() {
+  TrainingData::remove_unshareable_info();
+  _holder_mirror = nullptr;
+  _comp_deps.remove_unshareable_info();
+}
+
+void MethodTrainingData::remove_unshareable_info() {
+  TrainingData::remove_unshareable_info();
+  if (_final_counters != nullptr) {
+    _final_counters->remove_unshareable_info();
+  }
+  if (_final_profile != nullptr) {
+    _final_profile->remove_unshareable_info();
+  }
+}
+
+void CompileTrainingData::remove_unshareable_info() {
+  TrainingData::remove_unshareable_info();
+  _init_deps.remove_unshareable_info();
+  _ci_records.remove_unshareable_info();
+  _init_deps_left = compute_init_deps_left(true);
+}
+
+#endif // INCLUDE_CDS
diff a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/trainingData.hpp
@@ -0,0 +1,795 @@
+/*
+ * Copyright (c) 2023, 2024, 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.
+ *
+ */
+
+#ifndef SHARE_OOPS_TRAININGDATA_HPP
+#define SHARE_OOPS_TRAININGDATA_HPP
+
+#include "classfile/classLoaderData.hpp"
+#include "classfile/compactHashtable.hpp"
+#include "compiler/compilerDefinitions.hpp"
+#include "compiler/compiler_globals.hpp"
+#include "memory/allocation.hpp"
+#include "memory/metaspaceClosure.hpp"
+#include "oops/instanceKlass.hpp"
+#include "runtime/handles.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "utilities/count_leading_zeros.hpp"
+#include "utilities/resizeableResourceHash.hpp"
+
+class ciEnv;
+class ciBaseObject;
+class CompileTask;
+class xmlStream;
+class CompileTrainingData;
+class KlassTrainingData;
+class MethodTrainingData;
+class TrainingDataDumper;
+class TrainingDataSetLocker;
+class DumpTimeTrainingDataInfo;
+class RunTimeClassInfo;
+
+class TrainingData : public Metadata {
+  friend KlassTrainingData;
+  friend MethodTrainingData;
+  friend CompileTrainingData;
+ public:
+  class Key {
+    mutable Metadata* _meta;
+    // These guys can get to my constructors:
+    friend TrainingData;
+    friend KlassTrainingData;
+    friend MethodTrainingData;
+    friend CompileTrainingData;
+
+    // The empty key
+    Key() : _meta(nullptr) { }
+    bool is_empty() const { return _meta == nullptr; }
+  public:
+    Key(Metadata* meta) : _meta(meta) { }
+
+    static bool can_compute_cds_hash(const Key* const& k);
+    static uint cds_hash(const Key* const& k);
+    static unsigned hash(const Key* const& k) {
+      return primitive_hash(k->meta());
+    }
+    static bool equals(const Key* const& k1, const Key* const& k2) {
+      return k1->meta() == k2->meta();
+    }
+    static inline bool equals(TrainingData* value, const TrainingData::Key* key, int unused) {
+      return equals(value->key(), key);
+    }
+    int cmp(const Key* that) const {
+      auto m1 = this->meta();
+      auto m2 = that->meta();
+      if (m1 < m2) return -1;
+      if (m1 > m2) return +1;
+      return 0;
+    }
+    Metadata* meta() const { return _meta; }
+    void metaspace_pointers_do(MetaspaceClosure *iter);
+    void make_empty() const { _meta = nullptr; }
+  };
+
+  class TrainingDataLocker {
+    static volatile bool _snapshot;
+    static int _lock_mode;
+    static void lock() {
+      assert(_lock_mode != 0, "Forgot to call TrainingDataLocker::initialize()");
+      if (_lock_mode > 0) {
+        TrainingData_lock->lock();
+      }
+    }
+    static void unlock() {
+      if (_lock_mode > 0) {
+        TrainingData_lock->unlock();
+      }
+    }
+    static bool safely_locked() {
+      assert(_lock_mode != 0, "Forgot to call TrainingDataLocker::initialize()");
+      if (_lock_mode > 0) {
+        return TrainingData_lock->owned_by_self();
+      } else {
+        return true;
+      }
+    }
+  public:
+    static void snapshot() {
+      assert_locked();
+      _snapshot = true;
+    }
+    static bool can_add() {
+      assert_locked();
+      return !_snapshot;
+    }
+    static void initialize() {
+      _lock_mode = need_data() ? +1 : -1;   // if -1, we go lock-free
+    }
+    static void assert_locked() {
+      assert(safely_locked(), "use under TrainingDataLocker");
+    }
+    static void assert_can_add() {
+      assert(can_add(), "Cannot add TrainingData objects");
+    }
+    TrainingDataLocker() {
+      lock();
+    }
+    ~TrainingDataLocker() {
+      unlock();
+    }
+  };
+  class TrainingDataSet {
+    friend TrainingData;
+    ResizeableResourceHashtable<const Key*, TrainingData*,
+                                AnyObj::C_HEAP, MemTag::mtCompiler,
+                                &TrainingData::Key::hash,
+                                &TrainingData::Key::equals>
+      _table;
+
+  public:
+    template<typename... Arg>
+    TrainingDataSet(Arg... arg)
+      : _table(arg...) {
+    }
+    TrainingData* find(const Key* key) const {
+      TrainingDataLocker::assert_locked();
+      if (TrainingDataLocker::can_add()) {
+        auto res = _table.get(key);
+        return res == nullptr ? nullptr : *res;
+      }
+      return nullptr;
+    }
+    bool remove(const Key* key) {
+      return _table.remove(key);
+    }
+    TrainingData* install(TrainingData* tdata) {
+      TrainingDataLocker::assert_locked();
+      TrainingDataLocker::assert_can_add();
+      auto key = tdata->key();
+      if (key->is_empty())   return tdata;  // unkeyed TD not installed
+      bool created = false;
+      auto prior = _table.put_if_absent(key, tdata, &created);
+      if (prior == nullptr || *prior == tdata) {
+        return tdata;
+      }
+      assert(false, "no pre-existing elements allowed");
+      return *prior;
+    }
+    template<typename FN>
+    void iterate_all(FN fn) const { // lambda enabled API
+      return _table.iterate_all(fn);
+    }
+    int size() const { return _table.number_of_entries(); }
+
+    void verify() const {
+      TrainingDataLocker::assert_locked();
+      iterate_all([&](const TrainingData::Key* k, TrainingData* td) {
+        td->verify();
+      });
+    }
+  };
+
+  class Visitor {
+    ResizeableResourceHashtable<TrainingData*, bool> _visited;
+  public:
+    Visitor(unsigned size) : _visited(size, 0x3fffffff) { }
+    bool is_visited(TrainingData* td) {
+      return _visited.contains(td);
+    }
+    void visit(TrainingData* td) {
+      bool created;
+      _visited.put_if_absent(td, &created);
+    }
+  };
+
+  typedef OffsetCompactHashtable<const TrainingData::Key*, TrainingData*, TrainingData::Key::equals> TrainingDataDictionary;
+private:
+  Key _key;
+
+  // just forward all constructor arguments to the embedded key
+  template<typename... Arg>
+  TrainingData(Arg... arg)
+    : _key(arg...) { }
+
+  static TrainingDataSet _training_data_set;
+  static TrainingDataDictionary _archived_training_data_dictionary;
+  static TrainingDataDictionary _archived_training_data_dictionary_for_dumping;
+  typedef GrowableArrayCHeap<DumpTimeTrainingDataInfo, mtClassShared> DumptimeTrainingDataDictionary;
+  static DumptimeTrainingDataDictionary* _dumptime_training_data_dictionary;
+public:
+  // Returns the key under which this TD is installed, or else
+  // Key::EMPTY if it is not installed.
+  const Key* key() const { return &_key; }
+
+  static bool have_data() { return ReplayTraining;  } // Going to read
+  static bool need_data() { return RecordTraining;  } // Going to write
+
+  static TrainingDataSet* training_data_set() { return &_training_data_set; }
+  static TrainingDataDictionary* archived_training_data_dictionary() { return &_archived_training_data_dictionary; }
+
+  virtual MethodTrainingData*   as_MethodTrainingData()  const { return nullptr; }
+  virtual KlassTrainingData*    as_KlassTrainingData()   const { return nullptr; }
+  virtual CompileTrainingData*  as_CompileTrainingData() const { return nullptr; }
+  bool is_MethodTrainingData()  const { return as_MethodTrainingData()  != nullptr; }
+  bool is_KlassTrainingData()   const { return as_KlassTrainingData()   != nullptr; }
+  bool is_CompileTrainingData() const { return as_CompileTrainingData() != nullptr; }
+
+  virtual void prepare(Visitor& visitor) = 0;
+  virtual void cleanup(Visitor& visitor) = 0;
+
+  static void initialize();
+
+  static void verify();
+
+  // Widget for recording dependencies, as an N-to-M graph relation,
+  // possibly cyclic.
+  template<typename E>
+  class DepList : public StackObj {
+    GrowableArrayCHeap<E, mtCompiler>* _deps_dyn;
+    Array<E>*                          _deps;
+    // (hmm, could we have state-selected union of these two?)
+  public:
+    DepList() {
+      _deps_dyn = nullptr;
+      _deps = nullptr;
+    }
+
+    int length() const {
+      return (_deps_dyn != nullptr ? _deps_dyn->length()
+              : _deps   != nullptr ? _deps->length()
+              : 0);
+    }
+    E* adr_at(int i) const {
+      return (_deps_dyn != nullptr ? _deps_dyn->adr_at(i)
+              : _deps   != nullptr ? _deps->adr_at(i)
+              : nullptr);
+    }
+    E at(int i) const {
+      assert(i >= 0 && i < length(), "oob");
+      return *adr_at(i);
+    }
+    bool append_if_missing(E dep) {
+      if (_deps_dyn == nullptr) {
+        _deps_dyn = new GrowableArrayCHeap<E, mtCompiler>(10);
+        _deps_dyn->append(dep);
+        return true;
+      } else {
+        return _deps_dyn->append_if_missing(dep);
+      }
+    }
+    bool remove_if_existing(E dep) {
+      if (_deps_dyn != nullptr) {
+        return _deps_dyn->remove_if_existing(dep);
+      }
+      return false;
+    }
+    void clear() {
+      if (_deps_dyn != nullptr)  {
+        _deps_dyn->clear();
+      }
+    }
+    void append(E dep) {
+      if (_deps_dyn == nullptr) {
+        _deps_dyn = new GrowableArrayCHeap<E, mtCompiler>(10);
+      }
+      _deps_dyn->append(dep);
+    }
+    bool contains(E dep) {
+      for (int i = 0; i < length(); i++) {
+        if (dep == at(i)) {
+          return true; // found
+        }
+      }
+      return false; // not found
+    }
+
+#if INCLUDE_CDS
+    void remove_unshareable_info() {
+      _deps_dyn = nullptr;
+    }
+#endif
+    void prepare(ClassLoaderData* loader_data);
+    void metaspace_pointers_do(MetaspaceClosure *iter);
+  };
+
+  virtual void metaspace_pointers_do(MetaspaceClosure *iter);
+
+  static void init_dumptime_table(TRAPS);
+
+#if INCLUDE_CDS
+  virtual void remove_unshareable_info() {}
+  static void iterate_roots(MetaspaceClosure* it);
+  static void dump_training_data();
+  static void cleanup_training_data();
+  static void serialize_training_data(SerializeClosure* soc);
+  static void print_archived_training_data_on(outputStream* st);
+  static void write_training_data_dictionary(TrainingDataDictionary* dictionary);
+  static size_t estimate_size_for_archive();
+
+  static TrainingData* lookup_archived_training_data(const Key* k);
+#endif
+
+  static KlassTrainingData*  lookup_for(InstanceKlass* ik);
+  static MethodTrainingData* lookup_for(Method* m);
+
+  template<typename TrainingDataType, typename... ArgTypes>
+  static TrainingDataType* allocate(ArgTypes... args) {
+    assert(need_data() || have_data(), "");
+    if (TrainingDataLocker::can_add()) {
+      return new (mtClassShared) TrainingDataType(args...);
+    }
+    return nullptr;
+  }
+};
+
+class KlassTrainingData : public TrainingData {
+  friend TrainingData;
+  friend CompileTrainingData;
+
+  // Used by CDS. These classes need to access the private default constructor.
+  template <class T> friend class CppVtableTesterA;
+  template <class T> friend class CppVtableTesterB;
+  template <class T> friend class CppVtableCloner;
+
+  // cross-link to live klass, or null if not loaded or encountered yet
+  InstanceKlass* _holder;
+  jobject _holder_mirror;   // extra link to prevent unloading by GC
+
+  DepList<CompileTrainingData*> _comp_deps; // compiles that depend on me
+
+  KlassTrainingData();
+  KlassTrainingData(InstanceKlass* klass);
+
+  int comp_dep_count() const {
+    TrainingDataLocker::assert_locked();
+    return _comp_deps.length();
+  }
+  CompileTrainingData* comp_dep(int i) const {
+    TrainingDataLocker::assert_locked();
+    return _comp_deps.at(i);
+  }
+  void add_comp_dep(CompileTrainingData* ctd) {
+    TrainingDataLocker::assert_locked();
+     _comp_deps.append_if_missing(ctd);
+  }
+  void remove_comp_dep(CompileTrainingData* ctd) {
+    TrainingDataLocker::assert_locked();
+     _comp_deps.remove_if_existing(ctd);
+  }
+
+ public:
+  Symbol* name() const {
+    precond(has_holder());
+    return holder()->name();
+  }
+  Symbol* loader_name() const {
+    precond(has_holder());
+    return holder()->class_loader_name_and_id();
+  }
+  bool has_holder()       const { return _holder != nullptr; }
+  InstanceKlass* holder() const { return _holder; }
+
+  static KlassTrainingData* make(InstanceKlass* holder,
+                                 bool null_if_not_found = false);
+  static KlassTrainingData* find(InstanceKlass* holder) {
+    return make(holder, true);
+  }
+  virtual KlassTrainingData* as_KlassTrainingData() const { return const_cast<KlassTrainingData*>(this); };
+
+  ClassLoaderData* class_loader_data() {
+    assert(has_holder(), "");
+    return holder()->class_loader_data();
+  }
+  void notice_fully_initialized();
+
+  void print_on(outputStream* st, bool name_only) const;
+  virtual void print_on(outputStream* st) const { print_on(st, false); }
+  virtual void print_value_on(outputStream* st) const { print_on(st, true); }
+
+  virtual void prepare(Visitor& visitor);
+  virtual void cleanup(Visitor& visitor) NOT_CDS_RETURN;
+
+  MetaspaceObj::Type type() const {
+    return KlassTrainingDataType;
+  }
+
+#if INCLUDE_CDS
+  virtual void remove_unshareable_info();
+#endif
+
+  void metaspace_pointers_do(MetaspaceClosure *iter);
+
+  int size() const {
+    return (int)align_metadata_size(align_up(sizeof(KlassTrainingData), BytesPerWord)/BytesPerWord);
+  }
+
+  const char* internal_name() const {
+    return "{ klass training data }";
+  };
+
+  void verify();
+
+  static KlassTrainingData* allocate(InstanceKlass* holder) {
+    return TrainingData::allocate<KlassTrainingData>(holder);
+  }
+
+  template<typename FN>
+  void iterate_all_comp_deps(FN fn) const { // lambda enabled API
+    TrainingDataLocker l;
+    for (int i = 0; i < comp_dep_count(); i++) {
+      fn(comp_dep(i));
+    }
+  }
+};
+
+// Information about particular JIT tasks.
+class CompileTrainingData : public TrainingData {
+  friend TrainingData;
+  friend KlassTrainingData;
+
+  // Used by CDS. These classes need to access the private default constructor.
+  template <class T> friend class CppVtableTesterA;
+  template <class T> friend class CppVtableTesterB;
+  template <class T> friend class CppVtableCloner;
+
+  MethodTrainingData* _method;
+  const short _level;
+  const int _compile_id;
+
+  // classes that should be initialized before this JIT task runs
+  DepList<KlassTrainingData*> _init_deps;
+  volatile int _init_deps_left;
+
+public:
+  class ciRecords {
+    template <typename... Ts> class Arguments {
+    public:
+      bool operator==(const Arguments<>&) const { return true; }
+      void metaspace_pointers_do(MetaspaceClosure *iter) { }
+    };
+    template <typename T, typename... Ts> class Arguments<T, Ts...> {
+    private:
+      T _first;
+      Arguments<Ts...> _remaining;
+
+    public:
+      constexpr Arguments(const T& first, const Ts&... remaining) noexcept
+        : _first(first), _remaining(remaining...) {}
+      constexpr Arguments() noexcept : _first(), _remaining() {}
+      bool operator==(const Arguments<T, Ts...>& that) const {
+        return _first == that._first && _remaining == that._remaining;
+      }
+      template<typename U = T, std::enable_if_t<std::is_pointer<U>::value && std::is_base_of<MetaspaceObj, typename std::remove_pointer<U>::type>::value, int> = 0>
+      void metaspace_pointers_do(MetaspaceClosure *iter) {
+        iter->push(&_first);
+        _remaining.metaspace_pointers_do(iter);
+      }
+      template<typename U = T, std::enable_if_t<!(std::is_pointer<U>::value && std::is_base_of<MetaspaceObj, typename std::remove_pointer<U>::type>::value), int> = 0>
+      void metaspace_pointers_do(MetaspaceClosure *iter) {
+        _remaining.metaspace_pointers_do(iter);
+      }
+    };
+
+    template <typename ReturnType, typename... Args> class ciMemoizedFunction : public StackObj {
+    public:
+      class OptionalReturnType {
+        bool _valid;
+        ReturnType _result;
+      public:
+        OptionalReturnType(bool valid, const ReturnType& result) : _valid(valid), _result(result) {}
+        bool is_valid() const { return _valid; }
+        ReturnType result() const { return _result; }
+      };
+    private:
+      typedef Arguments<Args...> ArgumentsType;
+      class Record : public MetaspaceObj {
+        ReturnType    _result;
+        ArgumentsType _arguments;
+      public:
+        Record(const ReturnType& result, const ArgumentsType& arguments) : _result(result), _arguments(arguments) {}
+        Record() { }
+        ReturnType result() const { return _result; }
+        ArgumentsType arguments() const { return _arguments; }
+        bool operator==(const Record& that) { return _arguments == that._arguments; }
+        void metaspace_pointers_do(MetaspaceClosure *iter) { _arguments.metaspace_pointers_do(iter); }
+      };
+      DepList<Record> _data;
+    public:
+      OptionalReturnType find(const Args&... args) {
+        ArgumentsType a(args...);
+        for (int i = 0; i < _data.length(); i++) {
+          if (_data.at(i).arguments() == a) {
+            return OptionalReturnType(true, _data.at(i).result());
+          }
+        }
+        return OptionalReturnType(false, ReturnType());
+      }
+      bool append_if_missing(const ReturnType& result, const Args&... args) {
+        return _data.append_if_missing(Record(result, ArgumentsType(args...)));
+      }
+#if INCLUDE_CDS
+      void remove_unshareable_info() { _data.remove_unshareable_info(); }
+#endif
+      void prepare(ClassLoaderData* loader_data) {
+        _data.prepare(loader_data);
+      }
+      void metaspace_pointers_do(MetaspaceClosure *iter) {
+        _data.metaspace_pointers_do(iter);
+      }
+    };
+
+
+public:
+    typedef ciMemoizedFunction<int, MethodTrainingData*> ciMethod__inline_instructions_size_type;
+    ciMethod__inline_instructions_size_type ciMethod__inline_instructions_size;
+#if INCLUDE_CDS
+    void remove_unshareable_info() {
+      ciMethod__inline_instructions_size.remove_unshareable_info();
+    }
+#endif
+    void prepare(ClassLoaderData* loader_data) {
+      ciMethod__inline_instructions_size.prepare(loader_data);
+    }
+    void metaspace_pointers_do(MetaspaceClosure *iter) {
+      ciMethod__inline_instructions_size.metaspace_pointers_do(iter);
+    }
+  };
+
+private:
+  ciRecords _ci_records;
+
+  CompileTrainingData();
+  CompileTrainingData(MethodTrainingData* mtd,
+                      int level,
+                      int compile_id)
+      : TrainingData(),  // empty key
+        _method(mtd), _level(level), _compile_id(compile_id), _init_deps_left(0) { }
+public:
+  ciRecords& ci_records() { return _ci_records; }
+  static CompileTrainingData* make(CompileTask* task);
+
+  virtual CompileTrainingData* as_CompileTrainingData() const { return const_cast<CompileTrainingData*>(this); };
+
+  MethodTrainingData* method() const { return _method; }
+
+  int level() const { return _level; }
+
+  int compile_id() const { return _compile_id; }
+
+  int init_dep_count() const {
+    TrainingDataLocker::assert_locked();
+    return _init_deps.length();
+  }
+  KlassTrainingData* init_dep(int i) const {
+    TrainingDataLocker::assert_locked();
+    return _init_deps.at(i);
+  }
+  void add_init_dep(KlassTrainingData* ktd) {
+    TrainingDataLocker::assert_locked();
+    ktd->add_comp_dep(this);
+    _init_deps.append_if_missing(ktd);
+  }
+  void clear_init_deps() {
+    TrainingDataLocker::assert_locked();
+    for (int i = 0; i < _init_deps.length(); i++) {
+      _init_deps.at(i)->remove_comp_dep(this);
+    }
+    _init_deps.clear();
+  }
+  void dec_init_deps_left(KlassTrainingData* ktd);
+  int init_deps_left() const {
+    return Atomic::load(&_init_deps_left);
+  }
+  uint compute_init_deps_left(bool count_initialized = false);
+
+  void notice_inlined_method(CompileTask* task, const methodHandle& method);
+
+  // The JIT looks at classes and objects too and can depend on their state.
+  // These simple calls just report the *possibility* of an observation.
+  void notice_jit_observation(ciEnv* env, ciBaseObject* what);
+
+  virtual void prepare(Visitor& visitor);
+  virtual void cleanup(Visitor& visitor) NOT_CDS_RETURN;
+
+  void print_on(outputStream* st, bool name_only) const;
+  virtual void print_on(outputStream* st) const { print_on(st, false); }
+  virtual void print_value_on(outputStream* st) const { print_on(st, true); }
+
+#if INCLUDE_CDS
+  virtual void remove_unshareable_info();
+#endif
+
+  virtual void metaspace_pointers_do(MetaspaceClosure* iter);
+  virtual MetaspaceObj::Type type() const { return CompileTrainingDataType; }
+
+  virtual const char* internal_name() const {
+    return "{ compile training data }";
+  };
+
+  virtual int size() const {
+    return (int)align_metadata_size(align_up(sizeof(CompileTrainingData), BytesPerWord)/BytesPerWord);
+  }
+
+  void verify();
+
+  static CompileTrainingData* allocate(MethodTrainingData* mtd, int level, int compile_id) {
+    return TrainingData::allocate<CompileTrainingData>(mtd, level, compile_id);
+  }
+};
+
+// Record information about a method at the time compilation is requested.
+class MethodTrainingData : public TrainingData {
+  friend TrainingData;
+  friend CompileTrainingData;
+
+  // Used by CDS. These classes need to access the private default constructor.
+  template <class T> friend class CppVtableTesterA;
+  template <class T> friend class CppVtableTesterB;
+  template <class T> friend class CppVtableCloner;
+
+  KlassTrainingData* _klass;
+  Method* _holder;  // can be null
+  CompileTrainingData* _last_toplevel_compiles[CompLevel_count];
+  int _highest_top_level;
+  int _level_mask;  // bit-set of all possible levels
+  bool _was_inlined;
+  bool _was_toplevel;
+  // metadata snapshots of final state:
+  MethodCounters* _final_counters;
+  MethodData*     _final_profile;
+
+  MethodTrainingData();
+  MethodTrainingData(Method* method, KlassTrainingData* ktd) : TrainingData(method) {
+    _klass = ktd;
+    _holder = method;
+    for (int i = 0; i < CompLevel_count; i++) {
+      _last_toplevel_compiles[i] = nullptr;
+    }
+    _highest_top_level = CompLevel_none;
+    _level_mask = 0;
+    _was_inlined = _was_toplevel = false;
+  }
+
+  static int level_mask(int level) {
+    return ((level & 0xF) != level ? 0 : 1 << level);
+  }
+  static CompLevel highest_level(int mask) {
+    if (mask == 0)  return (CompLevel) 0;
+    int diff = (count_leading_zeros(level_mask(0)) - count_leading_zeros(mask));
+    return (CompLevel) diff;
+  }
+
+ public:
+  KlassTrainingData* klass()  const { return _klass; }
+  bool has_holder()           const { return _holder != nullptr; }
+  Method* holder()            const { return _holder; }
+  bool only_inlined()         const { return !_was_toplevel; }
+  bool never_inlined()        const { return !_was_inlined; }
+  bool saw_level(CompLevel l) const { return (_level_mask & level_mask(l)) != 0; }
+  int highest_level()         const { return highest_level(_level_mask); }
+  int highest_top_level()     const { return _highest_top_level; }
+  MethodData* final_profile() const { return _final_profile; }
+
+  Symbol* name() const {
+    precond(has_holder());
+    return holder()->name();
+  }
+  Symbol* signature() const {
+    precond(has_holder());
+    return holder()->signature();
+  }
+
+  CompileTrainingData* last_toplevel_compile(int level) const {
+    if (level > CompLevel_none) {
+      return _last_toplevel_compiles[level - 1];
+    }
+    return nullptr;
+  }
+
+  void notice_compilation(int level, bool inlined = false) {
+    if (inlined) {
+      _was_inlined = true;
+    } else {
+      _was_toplevel = true;
+    }
+    _level_mask |= level_mask(level);
+  }
+
+  static MethodTrainingData* make(const methodHandle& method,
+                                  bool null_if_not_found = false);
+  static MethodTrainingData* find(const methodHandle& method) {
+    return make(method, true);
+  }
+
+  virtual MethodTrainingData* as_MethodTrainingData() const {
+    return const_cast<MethodTrainingData*>(this);
+  };
+
+  void print_on(outputStream* st, bool name_only) const;
+  virtual void print_on(outputStream* st) const { print_on(st, false); }
+  virtual void print_value_on(outputStream* st) const { print_on(st, true); }
+
+  virtual void prepare(Visitor& visitor);
+  virtual void cleanup(Visitor& visitor) NOT_CDS_RETURN;
+
+  template<typename FN>
+  void iterate_all_compiles(FN fn) const { // lambda enabled API
+    for (int i = 0; i < CompLevel_count; i++) {
+      CompileTrainingData* ctd = _last_toplevel_compiles[i];
+      if (ctd != nullptr) {
+        fn(ctd);
+      }
+    }
+  }
+
+  virtual void metaspace_pointers_do(MetaspaceClosure* iter);
+  virtual MetaspaceObj::Type type() const { return MethodTrainingDataType; }
+
+#if INCLUDE_CDS
+  virtual void remove_unshareable_info();
+#endif
+
+  virtual int size() const {
+    return (int)align_metadata_size(align_up(sizeof(MethodTrainingData), BytesPerWord)/BytesPerWord);
+  }
+
+  virtual const char* internal_name() const {
+    return "{ method training data }";
+  };
+
+  void verify();
+
+  static MethodTrainingData* allocate(Method* m, KlassTrainingData* ktd) {
+    return TrainingData::allocate<MethodTrainingData>(m, ktd);
+  }
+};
+
+// CDS support
+
+class DumpTimeTrainingDataInfo {
+  TrainingData* _training_data;
+public:
+  DumpTimeTrainingDataInfo() : DumpTimeTrainingDataInfo(nullptr) {}
+
+  DumpTimeTrainingDataInfo(TrainingData* training_data)
+      : _training_data(training_data) {}
+
+  void metaspace_pointers_do(MetaspaceClosure* it) {
+    it->push(&_training_data);
+  }
+
+  TrainingData* training_data() {
+    return _training_data;
+  }
+};
+
+
+class TrainingDataPrinter : StackObj {
+  outputStream* _st;
+  int _index;
+public:
+  TrainingDataPrinter(outputStream* st) : _st(st), _index(0) {}
+  void do_value(TrainingData* record);
+};
+
+#endif // SHARE_OOPS_TRAININGDATA_HPP
diff a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp
--- a/src/hotspot/share/opto/c2compiler.cpp
+++ b/src/hotspot/share/opto/c2compiler.cpp
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "classfile/vmClasses.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilationMemoryStatistic.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "jfr/support/jfrIntrinsics.hpp"
 #include "opto/c2compiler.hpp"
@@ -59,10 +60,14 @@
 }
 const char* C2Compiler::retry_no_superword() {
   return "retry without SuperWord";
 }
 
+const char* C2Compiler::retry_no_clinit_barriers() {
+  return "retry without class initialization barriers";
+}
+
 void compiler_stubs_init(bool in_compiler_thread);
 
 bool C2Compiler::init_c2_runtime() {
 
 #ifdef ASSERT
@@ -94,11 +99,15 @@
   Compile::pd_compiler2_init();
 
   CompilerThread* thread = CompilerThread::current();
 
   HandleMark handle_mark(thread);
-  return OptoRuntime::generate(thread->env());
+  bool success = OptoRuntime::generate(thread->env());
+  if (success) {
+    SCCache::init_opto_table();
+  }
+  return success;
 }
 
 void C2Compiler::initialize() {
   assert(!CompilerConfig::is_c1_or_interpreter_only_no_jvmci(), "C2 compiler is launched, it's not c1/interpreter only mode");
   // The first compiler thread that gets here will initialize the
@@ -115,31 +124,52 @@
   }
 }
 
 void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive) {
   assert(is_initialized(), "Compiler thread must be initialized");
-
   CompilationMemoryStatisticMark cmsm(directive);
+  CompileTask* task = env->task();
+  if (install_code && task->is_scc()) {
+    bool success = SCCache::load_nmethod(env, target, entry_bci, this, CompLevel_full_optimization);
+    if (success) {
+      assert(task->is_success(), "sanity");
+      return;
+    }
+    if (!task->preload()) { // Do not mark entry if pre-loading failed - it can pass normal load
+      SCCache::invalidate(task->scc_entry()); // mark sca_entry as not entrant
+    }
+    if (SCCache::is_code_load_thread_on()) {
+      env->record_failure("Failed to load cached code");
+      // Bail out if failed to load cached code in SC thread
+      return;
+    }
+    task->clear_scc();
+  }
 
   bool subsume_loads = SubsumeLoads;
   bool do_escape_analysis = DoEscapeAnalysis;
   bool do_iterative_escape_analysis = DoEscapeAnalysis;
   bool do_reduce_allocation_merges = ReduceAllocationMerges && EliminateAllocations;
   bool eliminate_boxing = EliminateAutoBox;
   bool do_locks_coarsening = EliminateLocks;
   bool do_superword = UseSuperWord;
-
+  bool for_preload = (task->compile_reason() != CompileTask::Reason_Precompile) && // non-preload version is requested
+                     SCCache::gen_preload_code(target, entry_bci);
+  if (task->compile_reason() == CompileTask::Reason_PrecompileForPreload) {
+    assert(for_preload, "required");
+  }
   while (!env->failing()) {
     ResourceMark rm;
     // Attempt to compile while subsuming loads into machine instructions.
     Options options(subsume_loads,
                     do_escape_analysis,
                     do_iterative_escape_analysis,
                     do_reduce_allocation_merges,
                     eliminate_boxing,
                     do_locks_coarsening,
                     do_superword,
+                    for_preload,
                     install_code);
     Compile C(env, target, entry_bci, options, directive);
 
     // Check result and retry if appropriate.
     if (C.failure_reason() != nullptr) {
@@ -171,10 +201,15 @@
         assert(do_locks_coarsening, "must make progress");
         do_locks_coarsening = false;
         env->report_failure(C.failure_reason());
         continue;  // retry
       }
+      if (C.failure_reason_is(retry_no_clinit_barriers())) {
+        assert(for_preload, "must make progress");
+        for_preload = false;
+        continue;
+      }
       if (C.failure_reason_is(retry_no_superword())) {
         assert(do_superword, "must make progress");
         do_superword = false;
         env->report_failure(C.failure_reason());
         continue;  // retry
diff a/src/hotspot/share/opto/c2compiler.hpp b/src/hotspot/share/opto/c2compiler.hpp
--- a/src/hotspot/share/opto/c2compiler.hpp
+++ b/src/hotspot/share/opto/c2compiler.hpp
@@ -50,10 +50,11 @@
   static const char* retry_no_subsuming_loads();
   static const char* retry_no_escape_analysis();
   static const char* retry_no_iterative_escape_analysis();
   static const char* retry_no_reduce_allocation_merges();
   static const char* retry_no_locks_coarsening();
+  static const char* retry_no_clinit_barriers();
   static const char* retry_no_superword();
 
   // Print compilation timers and statistics
   void print_timers();
 
diff a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -633,10 +633,11 @@
                   _igv_idx(0),
                   _trace_opto_output(directive->TraceOptoOutputOption),
 #endif
                   _has_method_handle_invokes(false),
                   _clinit_barrier_on_entry(false),
+                  _has_clinit_barriers(false),
                   _stress_seed(0),
                   _comp_arena(mtCompiler),
                   _barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
                   _env(ci_env),
                   _directive(directive),
@@ -913,10 +914,11 @@
     _igv_idx(0),
     _trace_opto_output(directive->TraceOptoOutputOption),
 #endif
     _has_method_handle_invokes(false),
     _clinit_barrier_on_entry(false),
+    _has_clinit_barriers(false),
     _stress_seed(0),
     _comp_arena(mtCompiler),
     _barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
     _env(ci_env),
     _directive(directive),
@@ -1076,12 +1078,16 @@
   set_use_cmove(UseCMoveUnconditionally /* || do_vector_loop()*/); //TODO: consider do_vector_loop() mandate use_cmove unconditionally
   NOT_PRODUCT(if (use_cmove() && Verbose && has_method()) {tty->print("Compile::Init: use CMove without profitability tests for method %s\n",  method()->name()->as_quoted_ascii());})
 
   _max_node_limit = _directive->MaxNodeLimitOption;
 
-  if (VM_Version::supports_fast_class_init_checks() && has_method() && !is_osr_compilation() && method()->needs_clinit_barrier()) {
+  if (VM_Version::supports_fast_class_init_checks() && has_method() && !is_osr_compilation() &&
+      (method()->needs_clinit_barrier() || (do_clinit_barriers() && method()->is_static()))) {
     set_clinit_barrier_on_entry(true);
+    if (do_clinit_barriers()) {
+      set_has_clinit_barriers(true); // Entry clinit barrier is in prolog code.
+    }
   }
   if (debug_info()->recording_non_safepoints()) {
     set_node_note_array(new(comp_arena()) GrowableArray<Node_Notes*>
                         (comp_arena(), 8, 0, nullptr));
     set_default_node_notes(Node_Notes::make(this));
@@ -4049,10 +4055,13 @@
 // Report if there are too many traps at the current method and bci.
 // Return true if there was a trap, and/or PerMethodTrapLimit is exceeded.
 bool Compile::too_many_traps(ciMethod* method,
                              int bci,
                              Deoptimization::DeoptReason reason) {
+  if (method->has_trap_at(bci)) {
+    return true;
+  }
   ciMethodData* md = method->method_data();
   if (md->is_empty()) {
     // Assume the trap has not occurred, or that it occurred only
     // because of a transient condition during start-up in the interpreter.
     return false;
@@ -4163,14 +4172,14 @@
 bool Compile::needs_clinit_barrier(ciField* field, ciMethod* accessing_method) {
   return field->is_static() && needs_clinit_barrier(field->holder(), accessing_method);
 }
 
 bool Compile::needs_clinit_barrier(ciInstanceKlass* holder, ciMethod* accessing_method) {
-  if (holder->is_initialized()) {
+  if (holder->is_initialized() && !do_clinit_barriers()) {
     return false;
   }
-  if (holder->is_being_initialized()) {
+  if (holder->is_being_initialized() || do_clinit_barriers()) {
     if (accessing_method->holder() == holder) {
       // Access inside a class. The barrier can be elided when access happens in <clinit>,
       // <init>, or a static method. In all those cases, there was an initialization
       // barrier on the holder klass passed.
       if (accessing_method->is_static_initializer() ||
@@ -4555,11 +4564,11 @@
   }
 }
 
 void Compile::log_late_inline(CallGenerator* cg) {
   if (log() != nullptr) {
-    log()->head("late_inline method='%d'  inline_id='" JLONG_FORMAT "'", log()->identify(cg->method()),
+    log()->head("late_inline method='%d' inline_id='" JLONG_FORMAT "'", log()->identify(cg->method()),
                 cg->unique_id());
     JVMState* p = cg->call_node()->jvms();
     while (p != nullptr) {
       log()->elem("jvms bci='%d' method='%d'", p->bci(), log()->identify(p->method()));
       p = p->caller();
diff a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp
--- a/src/hotspot/share/opto/compile.hpp
+++ b/src/hotspot/share/opto/compile.hpp
@@ -180,27 +180,30 @@
   const bool _do_escape_analysis;    // Do escape analysis.
   const bool _do_iterative_escape_analysis;  // Do iterative escape analysis.
   const bool _do_reduce_allocation_merges;  // Do try to reduce allocation merges.
   const bool _eliminate_boxing;      // Do boxing elimination.
   const bool _do_locks_coarsening;   // Do locks coarsening
+  const bool _for_preload;           // Generate code for preload (before Java method execution), do class init barriers
   const bool _do_superword;          // Do SuperWord
   const bool _install_code;          // Install the code that was compiled
  public:
   Options(bool subsume_loads,
           bool do_escape_analysis,
           bool do_iterative_escape_analysis,
           bool do_reduce_allocation_merges,
           bool eliminate_boxing,
           bool do_locks_coarsening,
           bool do_superword,
+          bool for_preload,
           bool install_code) :
           _subsume_loads(subsume_loads),
           _do_escape_analysis(do_escape_analysis),
           _do_iterative_escape_analysis(do_iterative_escape_analysis),
           _do_reduce_allocation_merges(do_reduce_allocation_merges),
           _eliminate_boxing(eliminate_boxing),
           _do_locks_coarsening(do_locks_coarsening),
+          _for_preload(for_preload),
           _do_superword(do_superword),
           _install_code(install_code) {
   }
 
   static Options for_runtime_stub() {
@@ -209,10 +212,11 @@
        /* do_escape_analysis = */ false,
        /* do_iterative_escape_analysis = */ false,
        /* do_reduce_allocation_merges = */ false,
        /* eliminate_boxing = */ false,
        /* do_lock_coarsening = */ false,
+       /* for_preload = */ false,
        /* do_superword = */ true,
        /* install_code = */ true
     );
   }
 };
@@ -355,10 +359,11 @@
   // JSR 292
   bool                  _has_method_handle_invokes; // True if this method has MethodHandle invokes.
   bool                  _has_monitors;          // Metadata transfered to nmethod to enable Continuations lock-detection fastpath
   bool                  _has_scoped_access;     // For shared scope closure
   bool                  _clinit_barrier_on_entry; // True if clinit barrier is needed on nmethod entry
+  bool                  _has_clinit_barriers;   // True if compiled code has clinit barriers
   int                   _loop_opts_cnt;         // loop opts round
   uint                  _stress_seed;           // Seed for stress testing
 
   // Compilation environment.
   Arena                 _comp_arena;            // Arena with lifetime equivalent to Compile
@@ -599,10 +604,13 @@
   bool              should_install_code() const { return _options._install_code; }
   /** Do locks coarsening. */
   bool              do_locks_coarsening() const { return _options._do_locks_coarsening; }
   bool              do_superword() const        { return _options._do_superword; }
 
+  bool              do_clinit_barriers()  const { return _options._for_preload; }
+  bool              for_preload()         const { return _options._for_preload; }
+
   // Other fixed compilation parameters.
   ciMethod*         method() const              { return _method; }
   int               entry_bci() const           { return _entry_bci; }
   bool              is_osr_compilation() const  { return _entry_bci != InvocationEntryBci; }
   bool              is_method_compilation() const { return (_method != nullptr && !_method->flags().is_native()); }
@@ -677,10 +685,12 @@
   void          set_clinit_barrier_on_entry(bool z) { _clinit_barrier_on_entry = z; }
   bool              has_monitors() const         { return _has_monitors; }
   void          set_has_monitors(bool v)         { _has_monitors = v; }
   bool              has_scoped_access() const    { return _has_scoped_access; }
   void          set_has_scoped_access(bool v)    { _has_scoped_access = v; }
+  bool              has_clinit_barriers()        { return _has_clinit_barriers; }
+  void          set_has_clinit_barriers(bool z)  { _has_clinit_barriers = z; }
 
   // check the CompilerOracle for special behaviours for this compile
   bool          method_has_option(CompileCommandEnum option) {
     return method() != nullptr && method()->has_option(option);
   }
diff a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp
--- a/src/hotspot/share/opto/doCall.cpp
+++ b/src/hotspot/share/opto/doCall.cpp
@@ -94,10 +94,12 @@
   const bool is_virtual_or_interface = (bytecode == Bytecodes::_invokevirtual) ||
                                        (bytecode == Bytecodes::_invokeinterface) ||
                                        (orig_callee->intrinsic_id() == vmIntrinsics::_linkToVirtual) ||
                                        (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface);
 
+  callee->ensure_method_data(true);
+
   // Dtrace currently doesn't work unless all calls are vanilla
   if (env()->dtrace_method_probes()) {
     allow_inline = false;
   }
 
diff a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp
--- a/src/hotspot/share/opto/graphKit.cpp
+++ b/src/hotspot/share/opto/graphKit.cpp
@@ -3004,10 +3004,83 @@
   }
   speculating = false;
   return false;
 }
 
+void GraphKit::guard_klass_is_initialized(Node* klass) {
+  assert(ClassInitBarrierMode > 0, "no barriers");
+  int init_state_off = in_bytes(InstanceKlass::init_state_offset());
+  Node* adr = basic_plus_adr(top(), klass, init_state_off);
+  Node* init_state = LoadNode::make(_gvn, nullptr, immutable_memory(), adr,
+                                    adr->bottom_type()->is_ptr(), TypeInt::BYTE,
+                                    T_BYTE, MemNode::unordered);
+  init_state = _gvn.transform(init_state);
+
+  Node* initialized_state = makecon(TypeInt::make(InstanceKlass::fully_initialized));
+
+  Node* chk = _gvn.transform(new CmpINode(initialized_state, init_state));
+  Node* tst = _gvn.transform(new BoolNode(chk, BoolTest::eq));
+
+  switch (ClassInitBarrierMode) {
+    case 1: { // uncommon trap on slow path
+      BuildCutout unless(this, tst, PROB_MAX);
+      // Do not deoptimize this nmethod. Go to Interpreter to initialize class.
+      uncommon_trap(Deoptimization::Reason_uninitialized, Deoptimization::Action_none);
+      break;
+    }
+    case 2: { // runtime call on slow path
+      if (StressClassInitBarriers) {
+        tst = makecon(TypeInt::ZERO); // always go through slow path
+      }
+      IfNode* iff = create_and_xform_if(control(), tst, PROB_MAX, COUNT_UNKNOWN);
+//    IfNode* iff = create_and_map_if(control(), tst, PROB_MAX, COUNT_UNKNOWN);
+
+      RegionNode* r = new RegionNode(3);
+      r->init_req(1, _gvn.transform(new IfTrueNode(iff)));
+
+      set_control(_gvn.transform(new IfFalseNode(iff)));
+
+      if (!stopped()) {
+        kill_dead_locals();
+
+        Node* call = make_runtime_call(RC_NO_LEAF,
+                                       OptoRuntime::class_init_barrier_Type(),
+                                       OptoRuntime::class_init_barrier_Java(),
+                                       nullptr, TypePtr::BOTTOM,
+                                       klass);
+        // Deoptimization during class init barrier execution should trigger current bytecode reexecution.
+        call->jvms()->set_should_reexecute(true);
+
+        // FIXME: deoptimize for now. deoptimize=false doesn't work with late inlining yet.
+        // Parse::create_entry_map() introduces a barrier which uses distinct JVM state (*before* call).
+        // Compilation fails when distinct exception states are combined.
+        make_slow_call_ex(call, env()->Throwable_klass(), /*separate_io_proj=*/true, /*deoptimize=*/true);
+
+        Node* fast_io  = call->in(TypeFunc::I_O);
+        Node* fast_mem = call->in(TypeFunc::Memory);
+        // These two phis are pre-filled with copies of of the fast IO and Memory
+        Node* io_phi   = PhiNode::make(r, fast_io,  Type::ABIO);
+        Node* mem_phi  = PhiNode::make(r, fast_mem, Type::MEMORY, TypePtr::BOTTOM);
+
+        r->init_req(2, control());
+        io_phi->init_req(2, i_o());
+        mem_phi->init_req(2, reset_memory());
+
+        set_all_memory(_gvn.transform(mem_phi));
+        set_i_o(_gvn.transform(io_phi));
+      } else {
+        r->init_req(2, top());
+      }
+      set_control(_gvn.transform(r));
+      break;
+    }
+
+    default: fatal("unknown barrier mode: %d", ClassInitBarrierMode);
+  }
+  C->set_has_clinit_barriers(true);
+}
+
 void GraphKit::guard_klass_being_initialized(Node* klass) {
   int init_state_off = in_bytes(InstanceKlass::init_state_offset());
   Node* adr = basic_plus_adr(top(), klass, init_state_off);
   Node* init_state = LoadNode::make(_gvn, nullptr, immutable_memory(), adr,
                                     adr->bottom_type()->is_ptr(), TypeInt::BYTE,
@@ -3042,13 +3115,18 @@
     uncommon_trap(Deoptimization::Reason_uninitialized, Deoptimization::Action_none);
   }
 }
 
 void GraphKit::clinit_barrier(ciInstanceKlass* ik, ciMethod* context) {
+  if (C->do_clinit_barriers()) {
+    Node* klass = makecon(TypeKlassPtr::make(ik, Type::trust_interfaces));
+    guard_klass_is_initialized(klass);
+    return;
+  }
   if (ik->is_being_initialized()) {
     if (C->needs_clinit_barrier(ik, context)) {
-      Node* klass = makecon(TypeKlassPtr::make(ik));
+      Node* klass = makecon(TypeKlassPtr::make(ik, Type::trust_interfaces));
       guard_klass_being_initialized(klass);
       guard_init_thread(klass);
       insert_mem_bar(Op_MemBarCPUOrder);
     }
   } else if (ik->is_initialized()) {
diff a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp
--- a/src/hotspot/share/opto/graphKit.hpp
+++ b/src/hotspot/share/opto/graphKit.hpp
@@ -401,10 +401,11 @@
                        bool speculative = false);
 
   // Check the null_seen bit.
   bool seems_never_null(Node* obj, ciProfileData* data, bool& speculating);
 
+  void guard_klass_is_initialized(Node* klass);
   void guard_klass_being_initialized(Node* klass);
   void guard_init_thread(Node* klass);
 
   void clinit_barrier(ciInstanceKlass* ik, ciMethod* context);
 
@@ -631,10 +632,13 @@
                               Node* ctrl = nullptr);
 
   // Return a load of array element at idx.
   Node* load_array_element(Node* ary, Node* idx, const TypeAryPtr* arytype, bool set_ctrl);
 
+  //----------- runtime upcalls support ----------------
+  void install_on_method_entry_runtime_upcalls(ciMethod* method);
+
   //---------------- Dtrace support --------------------
   void make_dtrace_method_entry_exit(ciMethod* method, bool is_entry);
   void make_dtrace_method_entry(ciMethod* method) {
     make_dtrace_method_entry_exit(method, true);
   }
diff a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -68,11 +68,11 @@
   if (!m->is_loaded()) {
     // Do not attempt to inline unloaded methods.
     return nullptr;
   }
 
-  C2Compiler* compiler = (C2Compiler*)CompileBroker::compiler(CompLevel_full_optimization);
+  C2Compiler* compiler = (C2Compiler*)CompilerThread::current()->compiler();
   bool is_available = false;
 
   {
     // For calling is_intrinsic_supported and is_intrinsic_disabled_by_flag
     // the compiler must transition to '_thread_in_vm' state because both
diff a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp
--- a/src/hotspot/share/opto/loopnode.cpp
+++ b/src/hotspot/share/opto/loopnode.cpp
@@ -4352,21 +4352,20 @@
       log->tail("loop_tree");
       assert(loop->_next == nullptr, "what?");
     }
   } else if (loop != nullptr) {
     Node* head = loop->_head;
-    log->begin_head("loop");
-    log->print(" idx='%d' ", head->_idx);
-    if (loop->_irreducible) log->print("irreducible='1' ");
+    log->begin_head("loop idx='%d'", head->_idx);
+    if (loop->_irreducible) log->print(" irreducible='1'");
     if (head->is_Loop()) {
-      if (head->as_Loop()->is_inner_loop())        log->print("inner_loop='1' ");
-      if (head->as_Loop()->is_partial_peel_loop()) log->print("partial_peel_loop='1' ");
+      if (head->as_Loop()->is_inner_loop())        log->print(" inner_loop='1'");
+      if (head->as_Loop()->is_partial_peel_loop()) log->print(" partial_peel_loop='1'");
     } else if (head->is_CountedLoop()) {
       CountedLoopNode* cl = head->as_CountedLoop();
-      if (cl->is_pre_loop())  log->print("pre_loop='%d' ",  cl->main_idx());
-      if (cl->is_main_loop()) log->print("main_loop='%d' ", cl->_idx);
-      if (cl->is_post_loop()) log->print("post_loop='%d' ", cl->main_idx());
+      if (cl->is_pre_loop())  log->print(" pre_loop='%d'",  cl->main_idx());
+      if (cl->is_main_loop()) log->print(" main_loop='%d'", cl->_idx);
+      if (cl->is_post_loop()) log->print(" post_loop='%d'", cl->main_idx());
     }
     log->end_head();
     log_loop_tree_helper(root, loop->_child, log);
     log->tail("loop");
     log_loop_tree_helper(root, loop->_next, log);
diff a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp
--- a/src/hotspot/share/opto/output.cpp
+++ b/src/hotspot/share/opto/output.cpp
@@ -3400,18 +3400,16 @@
 
   return buf.insts_size();
 }
 
 void PhaseOutput::install() {
-  if (!C->should_install_code()) {
-    return;
-  } else if (C->stub_function() != nullptr) {
+  if (C->should_install_code() && C->stub_function() != nullptr) {
     install_stub(C->stub_name());
   } else {
     install_code(C->method(),
                  C->entry_bci(),
-                 CompileBroker::compiler2(),
+                 CompilerThread::current()->compiler(),
                  C->has_unsafe_access(),
                  SharedRuntime::is_wide_vector(C->max_vector_size()));
   }
 }
 
@@ -3452,19 +3450,31 @@
                                      frame_size_in_words(),
                                      oop_map_set(),
                                      &_handler_table,
                                      inc_table(),
                                      compiler,
+                                     C->has_clinit_barriers(),
+                                     C->for_preload(),
                                      has_unsafe_access,
                                      SharedRuntime::is_wide_vector(C->max_vector_size()),
                                      C->has_monitors(),
                                      C->has_scoped_access(),
-                                     0);
+                                     0,
+                                     C->should_install_code());
 
     if (C->log() != nullptr) { // Print code cache state into compiler log
       C->log()->code_cache_state();
     }
+    if (C->has_clinit_barriers()) {
+      assert(C->for_preload(), "sanity");
+      // Build second version of code without class initialization barriers
+      if (C->env()->task()->compile_reason() == CompileTask::Reason_PrecompileForPreload) {
+        // don't automatically precompile a barrier-free version unless explicitly asked
+      } else {
+        C->record_failure(C2Compiler::retry_no_clinit_barriers());
+      }
+    }
   }
 }
 void PhaseOutput::install_stub(const char* stub_name) {
   // Entry point will be accessed using stub_entry_point();
   if (code_buffer() == nullptr) {
diff a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp
--- a/src/hotspot/share/opto/parse.hpp
+++ b/src/hotspot/share/opto/parse.hpp
@@ -498,10 +498,11 @@
   void array_store(BasicType etype);
   // Helper function to compute array addressing
   Node* array_addressing(BasicType type, int vals, const Type*& elemtype);
 
   void clinit_deopt();
+  void clinit_barrier_on_inline();
 
   // Pass current map to exits
   void return_current(Node* value);
 
   // Register finalizers on return from Object.<init>
diff a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp
--- a/src/hotspot/share/opto/parse1.cpp
+++ b/src/hotspot/share/opto/parse1.cpp
@@ -37,10 +37,11 @@
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
 #include "opto/type.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/safepointMechanism.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "utilities/bitMap.inline.hpp"
 #include "utilities/copy.hpp"
 
@@ -1132,13 +1133,22 @@
 
   // clear current replaced nodes that are of no use from here on (map was cloned in build_exits).
   _caller->map()->delete_replaced_nodes();
 
   // If this is an inlined method, we may have to do a receiver null check.
-  if (_caller->has_method() && is_normal_parse() && !method()->is_static()) {
+  if (_caller->has_method() && is_normal_parse()) {
     GraphKit kit(_caller);
-    kit.null_check_receiver_before_call(method());
+    if (!method()->is_static()) {
+      kit.null_check_receiver_before_call(method());
+    } else if (C->do_clinit_barriers() && C->needs_clinit_barrier(method()->holder(), _caller->method())) {
+      ciMethod* declared_method = kit.method()->get_method_at_bci(kit.bci());
+      const int nargs = declared_method->arg_size();
+      kit.inc_sp(nargs);
+      Node* holder = makecon(TypeKlassPtr::make(method()->holder(), Type::trust_interfaces));
+      kit.guard_klass_is_initialized(holder);
+      kit.dec_sp(nargs);
+    }
     _caller = kit.transfer_exceptions_into_jvms();
     if (kit.stopped()) {
       _exits.add_exception_states_from(_caller);
       _exits.set_jvms(_caller);
       return nullptr;
@@ -1200,10 +1210,12 @@
 
   if (C->env()->dtrace_method_probes()) {
     make_dtrace_method_entry(method());
   }
 
+  install_on_method_entry_runtime_upcalls(method());
+
 #ifdef ASSERT
   // Narrow receiver type when it is too broad for the method being parsed.
   if (!method()->is_static()) {
     ciInstanceKlass* callee_holder = method()->holder();
     const Type* holder_type = TypeInstPtr::make(TypePtr::BotPTR, callee_holder, Type::trust_interfaces);
@@ -2168,10 +2180,13 @@
   set_control( _gvn.transform(result_rgn) );
 }
 
 // Add check to deoptimize once holder klass is fully initialized.
 void Parse::clinit_deopt() {
+  if (method()->holder()->is_initialized()) {
+    return; // in case do_clinit_barriers() is true
+  }
   assert(C->has_method(), "only for normal compilations");
   assert(depth() == 1, "only for main compiled method");
   assert(is_normal_parse(), "no barrier needed on osr entry");
   assert(!method()->holder()->is_not_initialized(), "initialization should have been started");
 
diff a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp
--- a/src/hotspot/share/opto/parse2.cpp
+++ b/src/hotspot/share/opto/parse2.cpp
@@ -1292,10 +1292,16 @@
     if (prob <= PROB_MIN)  prob_str = (prob == PROB_MIN) ? "min" : "never";
     char prob_str_buf[30];
     if (prob_str == nullptr) {
       jio_snprintf(prob_str_buf, sizeof(prob_str_buf), "%20.2f", prob);
       prob_str = prob_str_buf;
+      // The %20.2f adds many spaces to the string, to avoid some
+      // picky overflow warning as noted in 8211929.  But, 20 is the
+      // *minimum* width, not *maximum*, so it's not clear how this
+      // helps prevent overflow.  Looks like we were forced to work
+      // around a bug in gcc.  In any case, strip the blanks.
+      while (*prob_str == ' ')  ++prob_str;
     }
     C->log()->elem("branch target_bci='%d' taken='%d' not_taken='%d' cnt='%f' prob='%s'",
                    iter().get_dest(), taken, not_taken, cnt, prob_str);
   }
   return prob;
diff a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp
--- a/src/hotspot/share/opto/parseHelper.cpp
+++ b/src/hotspot/share/opto/parseHelper.cpp
@@ -30,12 +30,33 @@
 #include "opto/memnode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
+#include "runtime/runtimeUpcalls.hpp"
 #include "runtime/sharedRuntime.hpp"
 
+void GraphKit::install_on_method_entry_runtime_upcalls(ciMethod* method) {
+  MethodDetails method_details(method);
+  RuntimeUpcallInfo* upcall = RuntimeUpcalls::get_first_upcall(RuntimeUpcallType::onMethodEntry, method_details);
+  while (upcall != nullptr) {
+    // Get base of thread-local storage area
+    Node* thread = _gvn.transform( new ThreadLocalNode() );
+    kill_dead_locals();
+
+    // For some reason, this call reads only raw memory.
+    const TypeFunc *call_type   = OptoRuntime::runtime_up_call_Type();
+    const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM;
+    make_runtime_call(RC_LEAF | RC_NARROW_MEM,
+                      call_type, upcall->upcall_address(),
+                      upcall->upcall_name(), raw_adr_type,
+                      thread);
+
+    upcall = RuntimeUpcalls::get_next_upcall(RuntimeUpcallType::onMethodEntry, method_details, upcall);
+  }
+}
+
 //------------------------------make_dtrace_method_entry_exit ----------------
 // Dtrace -- record entry or exit of a method if compiled with dtrace support
 void GraphKit::make_dtrace_method_entry_exit(ciMethod* method, bool is_entry) {
   const TypeFunc *call_type    = OptoRuntime::dtrace_method_entry_exit_Type();
   address         call_address = is_entry ? CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry) :
diff a/src/hotspot/share/opto/rootnode.cpp b/src/hotspot/share/opto/rootnode.cpp
--- a/src/hotspot/share/opto/rootnode.cpp
+++ b/src/hotspot/share/opto/rootnode.cpp
@@ -29,10 +29,11 @@
 #include "opto/phaseX.hpp"
 #include "opto/regmask.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/subnode.hpp"
 #include "opto/type.hpp"
+#include "code/SCCache.hpp"
 
 //------------------------------Ideal------------------------------------------
 // Remove dead inputs
 Node *RootNode::Ideal(PhaseGVN *phase, bool can_reshape) {
   bool modified = false;
@@ -69,10 +70,11 @@
   init_req(TypeFunc::Control,  ctrl        );
   init_req(TypeFunc::I_O,      top);
   init_req(TypeFunc::Memory,   top);
   init_req(TypeFunc::FramePtr, frameptr    );
   init_req(TypeFunc::ReturnAdr,top);
+  SCCache::add_C_string(halt_reason);
 }
 
 const Type *HaltNode::bottom_type() const { return Type::BOTTOM; }
 uint HaltNode::size_of() const { return sizeof(*this); }
 
diff a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -30,10 +30,11 @@
 #include "code/nmethod.hpp"
 #include "code/pcDesc.hpp"
 #include "code/scopeDesc.hpp"
 #include "code/vtableStubs.hpp"
 #include "compiler/compileBroker.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/g1/g1HeapRegion.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gcLocker.hpp"
@@ -63,20 +64,23 @@
 #include "prims/jvmtiExport.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/stackWatermarkSet.hpp"
 #include "runtime/synchronizer.hpp"
 #include "runtime/threadCritical.hpp"
 #include "runtime/threadWXSetters.inline.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vframe_hp.hpp"
+#include "services/management.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/preserveException.hpp"
 
 
 // For debugging purposes:
@@ -98,20 +102,25 @@
 C2_STUBS_DO(C2_BLOB_FIELD_DEFINE, C2_STUB_FIELD_DEFINE, C2_JVMTI_STUB_FIELD_DEFINE)
 #undef C2_BLOB_FIELD_DEFINE
 #undef C2_STUB_FIELD_DEFINE
 #undef C2_JVMTI_STUB_FIELD_DEFINE
 
+
 #define C2_BLOB_NAME_DEFINE(name, type)  "C2 Runtime " # name "_blob",
 #define C2_STUB_NAME_DEFINE(name, f, t, r)  "C2 Runtime " # name,
 #define C2_JVMTI_STUB_NAME_DEFINE(name)  "C2 Runtime " # name,
 const char* OptoRuntime::_stub_names[] = {
   C2_STUBS_DO(C2_BLOB_NAME_DEFINE, C2_STUB_NAME_DEFINE, C2_JVMTI_STUB_NAME_DEFINE)
 };
 #undef C2_BLOB_NAME_DEFINE
 #undef C2_STUB_NAME_DEFINE
 #undef C2_JVMTI_STUB_NAME_DEFINE
 
+address OptoRuntime::_vtable_must_compile_Java                    = nullptr;
+
+PerfCounter* _perf_OptoRuntime_class_init_barrier_redundant_count = nullptr;
+
 // This should be called in an assertion at the start of OptoRuntime routines
 // which are entered from compiled code (all of them)
 #ifdef ASSERT
 static bool check_compiled_frame(JavaThread* thread) {
   assert(thread->last_frame().is_runtime_frame(), "cannot call runtime directly from compiled code");
@@ -172,10 +181,11 @@
                   true,                                                 \
                   false);                                               \
   if (STUB_FIELD_NAME(name) == nullptr) { return false; }               \
 
 bool OptoRuntime::generate(ciEnv* env) {
+  init_counters();
 
   C2_STUBS_DO(GEN_C2_BLOB, GEN_C2_STUB, GEN_C2_JVMTI_STUB)
 
   return true;
 }
@@ -199,11 +209,11 @@
                                    const char *name, int is_fancy_jump,
                                    bool pass_tls,
                                    bool return_pc) {
 
   // Matching the default directive, we currently have no method to match.
-  DirectiveSet* directive = DirectivesStack::getDefaultDirective(CompileBroker::compiler(CompLevel_full_optimization));
+  DirectiveSet* directive = DirectivesStack::getDefaultDirective(CompilerThread::current()->compiler());
   ResourceMark rm;
   Compile C(env, gen, C_function, name, is_fancy_jump, pass_tls, return_pc, directive);
   DirectivesStack::release(directive);
   return  C.stub_entry_point();
 }
@@ -242,11 +252,11 @@
 //=============================allocation======================================
 // We failed the fast-path allocation.  Now we need to do a scavenge or GC
 // and try allocation again.
 
 // object allocation
-JRT_BLOCK_ENTRY(void, OptoRuntime::new_instance_C(Klass* klass, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, new_instance_C, OptoRuntime::new_instance_C(Klass* klass, JavaThread* current))
   JRT_BLOCK;
 #ifndef PRODUCT
   SharedRuntime::_new_instance_ctr++;         // new instance requires GC
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
@@ -280,11 +290,11 @@
   SharedRuntime::on_slowpath_allocation_exit(current);
 JRT_END
 
 
 // array allocation
-JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_C(Klass* array_type, int len, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, new_array_C, OptoRuntime::new_array_C(Klass* array_type, int len, JavaThread* current))
   JRT_BLOCK;
 #ifndef PRODUCT
   SharedRuntime::_new_array_ctr++;            // new array requires GC
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
@@ -317,11 +327,11 @@
   // inform GC that we won't do card marks for initializing writes.
   SharedRuntime::on_slowpath_allocation_exit(current);
 JRT_END
 
 // array allocation without zeroing
-JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_nozero_C(Klass* array_type, int len, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, new_array_nozero_C, OptoRuntime::new_array_nozero_C(Klass* array_type, int len, JavaThread* current))
   JRT_BLOCK;
 #ifndef PRODUCT
   SharedRuntime::_new_array_ctr++;            // new array requires GC
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
@@ -369,11 +379,11 @@
 JRT_END
 
 // Note: multianewarray for one dimension is handled inline by GraphKit::new_array.
 
 // multianewarray for 2 dimensions
-JRT_ENTRY(void, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int len2, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarray2_C, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int len2, JavaThread* current))
 #ifndef PRODUCT
   SharedRuntime::_multi2_ctr++;                // multianewarray for 1 dimension
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
@@ -385,11 +395,11 @@
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
 // multianewarray for 3 dimensions
-JRT_ENTRY(void, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int len2, int len3, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarray3_C, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int len2, int len3, JavaThread* current))
 #ifndef PRODUCT
   SharedRuntime::_multi3_ctr++;                // multianewarray for 1 dimension
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
@@ -402,11 +412,11 @@
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
 // multianewarray for 4 dimensions
-JRT_ENTRY(void, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int len2, int len3, int len4, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarray4_C, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int len2, int len3, int len4, JavaThread* current))
 #ifndef PRODUCT
   SharedRuntime::_multi4_ctr++;                // multianewarray for 1 dimension
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
@@ -438,11 +448,11 @@
   oop obj = ArrayKlass::cast(elem_type)->multi_allocate(5, dims, THREAD);
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
-JRT_ENTRY(void, OptoRuntime::multianewarrayN_C(Klass* elem_type, arrayOopDesc* dims, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarrayN_C, OptoRuntime::multianewarrayN_C(Klass* elem_type, arrayOopDesc* dims, JavaThread* current))
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
   assert(oop(dims)->is_typeArray(), "not an array");
 
   ResourceMark rm;
@@ -456,11 +466,11 @@
   oop obj = ArrayKlass::cast(elem_type)->multi_allocate(len, c_dims, THREAD);
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
-JRT_BLOCK_ENTRY(void, OptoRuntime::monitor_notify_C(oopDesc* obj, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, monitor_notify_C, OptoRuntime::monitor_notify_C(oopDesc* obj, JavaThread* current))
 
   // Very few notify/notifyAll operations find any threads on the waitset, so
   // the dominant fast-path is to simply return.
   // Relatedly, it's critical that notify/notifyAll be fast in order to
   // reduce lock hold times.
@@ -478,11 +488,11 @@
   Handle h_obj(current, obj);
   ObjectSynchronizer::notify(h_obj, CHECK);
   JRT_BLOCK_END;
 JRT_END
 
-JRT_BLOCK_ENTRY(void, OptoRuntime::monitor_notifyAll_C(oopDesc* obj, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, monitor_notifyAll_C, OptoRuntime::monitor_notifyAll_C(oopDesc* obj, JavaThread* current))
 
   if (!SafepointSynchronize::is_synchronizing() ) {
     if (ObjectSynchronizer::quick_notify(obj, current, true)) {
       return;
     }
@@ -1542,11 +1552,11 @@
 static void trace_exception(outputStream* st, oop exception_oop, address exception_pc, const char* msg);
 
 // The method is an entry that is always called by a C++ method not
 // directly from compiled code. Compiled code will call the C++ method following.
 // We can't allow async exception to be installed during  exception processing.
-JRT_ENTRY_NO_ASYNC(address, OptoRuntime::handle_exception_C_helper(JavaThread* current, nmethod* &nm))
+JRT_ENTRY_NO_ASYNC_PROF(address, OptoRuntime, handle_exception_C_helper, OptoRuntime::handle_exception_C_helper(JavaThread* current, nmethod* &nm))
   // The frame we rethrow the exception to might not have been processed by the GC yet.
   // The stack watermark barrier takes care of detecting that and ensuring the frame
   // has updated oops.
   StackWatermarkSet::after_unwind(current);
 
@@ -1818,10 +1828,24 @@
   const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
 
   return TypeFunc::make(domain,range);
 }
 
+const TypeFunc *OptoRuntime::class_init_barrier_Type() {
+  // create input type (domain)
+  const Type** fields = TypeTuple::fields(1);
+  fields[TypeFunc::Parms+0] = TypeKlassPtr::NOTNULL;
+  // // The JavaThread* is passed to each routine as the last argument
+  // fields[TypeFunc::Parms+1] = TypeRawPtr::NOTNULL;  // JavaThread *; Executing thread
+  const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+1, fields);
+
+  // create result type (range)
+  fields = TypeTuple::fields(0);
+  const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields);
+  return TypeFunc::make(domain,range);
+}
+
 #if INCLUDE_JFR
 const TypeFunc *OptoRuntime::class_id_load_barrier_Type() {
   // create input type (domain)
   const Type **fields = TypeTuple::fields(1);
   fields[TypeFunc::Parms+0] = TypeInstPtr::KLASS;
@@ -1834,10 +1858,26 @@
 
   return TypeFunc::make(domain,range);
 }
 #endif
 
+//-----------------------------------------------------------------------------
+// runtime upcall support
+const TypeFunc *OptoRuntime::runtime_up_call_Type() {
+  // create input type (domain)
+  const Type **fields = TypeTuple::fields(1);
+  fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM; // Thread-local storage
+  const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1,fields);
+
+  // create result type (range)
+  fields = TypeTuple::fields(0);
+
+  const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
+
+  return TypeFunc::make(domain,range);
+}
+
 //-----------------------------------------------------------------------------
 // Dtrace support.  entry and exit probes have the same signature
 const TypeFunc *OptoRuntime::dtrace_method_entry_exit_Type() {
   // create input type (domain)
   const Type **fields = TypeTuple::fields(2);
@@ -1868,16 +1908,25 @@
 
   return TypeFunc::make(domain,range);
 }
 
 
-JRT_ENTRY_NO_ASYNC(void, OptoRuntime::register_finalizer_C(oopDesc* obj, JavaThread* current))
+JRT_ENTRY_NO_ASYNC_PROF(void, OptoRuntime, register_finalizer_C, OptoRuntime::register_finalizer_C(oopDesc* obj, JavaThread* current))
   assert(oopDesc::is_oop(obj), "must be a valid oop");
   assert(obj->klass()->has_finalizer(), "shouldn't be here otherwise");
   InstanceKlass::register_finalizer(instanceOop(obj), CHECK);
 JRT_END
 
+JRT_ENTRY_NO_ASYNC_PROF(void, OptoRuntime, class_init_barrier_C, OptoRuntime::class_init_barrier_C(Klass* k, JavaThread* current))
+  InstanceKlass* ik = InstanceKlass::cast(k);
+  if (ik->should_be_initialized()) {
+    ik->initialize(CHECK);
+  } else if (UsePerfData) {
+    _perf_OptoRuntime_class_init_barrier_redundant_count->inc();
+  }
+JRT_END
+
 //-----------------------------------------------------------------------------
 
 NamedCounter * volatile OptoRuntime::_named_counters = nullptr;
 
 //
@@ -1974,5 +2023,71 @@
   tempst.print(" at " INTPTR_FORMAT,  p2i(exception_pc));
   tempst.print("]");
 
   st->print_raw_cr(tempst.freeze());
 }
+
+#define DO_COUNTERS2(macro2, macro1) \
+  macro2(OptoRuntime, new_instance_C) \
+  macro2(OptoRuntime, new_array_C) \
+  macro2(OptoRuntime, new_array_nozero_C) \
+  macro2(OptoRuntime, multianewarray2_C) \
+  macro2(OptoRuntime, multianewarray3_C) \
+  macro2(OptoRuntime, multianewarray4_C) \
+  macro2(OptoRuntime, multianewarrayN_C) \
+  macro2(OptoRuntime, monitor_notify_C) \
+  macro2(OptoRuntime, monitor_notifyAll_C) \
+  macro2(OptoRuntime, handle_exception_C_helper) \
+  macro2(OptoRuntime, register_finalizer_C) \
+  macro2(OptoRuntime, class_init_barrier_C) \
+  macro1(OptoRuntime, class_init_barrier_redundant)
+
+#define INIT_COUNTER_TIME_AND_CNT(sub, name) \
+  NEWPERFTICKCOUNTERS(_perf_##sub##_##name##_timer, SUN_CI, #sub "::" #name); \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_CI, #sub "::" #name "_count");
+
+#define INIT_COUNTER_CNT(sub, name) \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_CI, #sub "::" #name "_count");
+
+void OptoRuntime::init_counters() {
+  assert(CompilerConfig::is_c2_enabled(), "");
+
+  if (UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS2(INIT_COUNTER_TIME_AND_CNT, INIT_COUNTER_CNT)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("jvm_perf_init failed unexpectedly");
+    }
+  }
+}
+#undef INIT_COUNTER_TIME_AND_CNT
+#undef INIT_COUNTER_CNT
+
+#define PRINT_COUNTER_TIME_AND_CNT(sub, name) { \
+  jlong count = _perf_##sub##_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-50s = " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) (" JLONG_FORMAT_W(5) " events)", #sub "::" #name, \
+                 _perf_##sub##_##name##_timer->elapsed_counter_value_us(), \
+                 _perf_##sub##_##name##_timer->thread_counter_value_us(), \
+                 count); \
+  }}
+
+#define PRINT_COUNTER_CNT(sub, name) { \
+  jlong count = _perf_##sub##_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-30s = " JLONG_FORMAT_W(5) " events", #name, count); \
+  }}
+
+void OptoRuntime::print_counters_on(outputStream* st) {
+  if (UsePerfData && ProfileRuntimeCalls && CompilerConfig::is_c2_enabled()) {
+    DO_COUNTERS2(PRINT_COUNTER_TIME_AND_CNT, PRINT_COUNTER_CNT)
+  } else {
+    st->print_cr("  OptoRuntime: no info (%s is disabled)",
+                 (!CompilerConfig::is_c2_enabled() ? "C2" : (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData")));
+  }
+}
+
+#undef PRINT_COUNTER_TIME_AND_CNT
+#undef PRINT_COUNTER_CNT
+#undef DO_COUNTERS2
diff a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp
--- a/src/hotspot/share/opto/runtime.hpp
+++ b/src/hotspot/share/opto/runtime.hpp
@@ -113,11 +113,11 @@
 #undef C2_STUB_ID_ENUM_DECLARE
 #undef C2_JVMTI_STUB_ID_ENUM_DECLARE
 
 class OptoRuntime : public AllStatic {
   friend class Matcher;  // allow access to stub names
-
+  friend class SCAddressTable;
  private:
   // declare opto stub address/blob holder static fields
 #define C2_BLOB_FIELD_DECLARE(name, type) \
   static type        BLOB_FIELD_NAME(name);
 #define C2_STUB_FIELD_NAME(name) _ ## name ## _Java
@@ -137,10 +137,12 @@
   static const char *_stub_names[];
 
   // define stubs
   static address generate_stub(ciEnv* ci_env, TypeFunc_generator gen, address C_function, const char* name, int is_fancy_jump, bool pass_tls, bool return_pc);
 
+  static address _vtable_must_compile_Java;
+
   //
   // Implementation of runtime methods
   // =================================
 
   // Allocate storage for a Java instance.
@@ -187,11 +189,11 @@
 
   static void generate_uncommon_trap_blob(void);
   static void generate_exception_blob();
 
   static void register_finalizer_C(oopDesc* obj, JavaThread* current);
-
+  static void class_init_barrier_C(Klass* k, JavaThread* current);
  public:
 
   static bool is_callee_saved_register(MachRegisterNumbers reg);
 
   // One time only generate runtime code stubs. Returns true
@@ -215,16 +217,18 @@
   static address multianewarray2_Java()                  { return _multianewarray2_Java; }
   static address multianewarray3_Java()                  { return _multianewarray3_Java; }
   static address multianewarray4_Java()                  { return _multianewarray4_Java; }
   static address multianewarray5_Java()                  { return _multianewarray5_Java; }
   static address multianewarrayN_Java()                  { return _multianewarrayN_Java; }
+  static address vtable_must_compile_stub()              { return _vtable_must_compile_Java; }
   static address complete_monitor_locking_Java()         { return _complete_monitor_locking_Java; }
   static address monitor_notify_Java()                   { return _monitor_notify_Java; }
   static address monitor_notifyAll_Java()                { return _monitor_notifyAll_Java; }
 
   static address slow_arraycopy_Java()                   { return _slow_arraycopy_Java; }
   static address register_finalizer_Java()               { return _register_finalizer_Java; }
+  static address class_init_barrier_Java()               { return _class_init_barrier_Java; }
 #if INCLUDE_JVMTI
   static address notify_jvmti_vthread_start()            { return _notify_jvmti_vthread_start; }
   static address notify_jvmti_vthread_end()              { return _notify_jvmti_vthread_end; }
   static address notify_jvmti_vthread_mount()            { return _notify_jvmti_vthread_mount; }
   static address notify_jvmti_vthread_unmount()          { return _notify_jvmti_vthread_unmount; }
@@ -323,15 +327,20 @@
   // leaf on stack replacement interpreter accessor types
   static const TypeFunc* osr_end_Type();
 
   static const TypeFunc* register_finalizer_Type();
 
+  static const TypeFunc* class_init_barrier_Type();
+
   JFR_ONLY(static const TypeFunc* class_id_load_barrier_Type();)
 #if INCLUDE_JVMTI
   static const TypeFunc* notify_jvmti_vthread_Type();
 #endif
 
+  // runtime upcalls support
+  static const TypeFunc* runtime_up_call_Type();
+
   // Dtrace support
   static const TypeFunc* dtrace_method_entry_exit_Type();
   static const TypeFunc* dtrace_object_alloc_Type();
 
  private:
@@ -343,8 +352,11 @@
  static NamedCounter* new_named_counter(JVMState* jvms, NamedCounter::CounterTag tag);
 
  // dumps all the named counters
  static void          print_named_counters();
 
+ public:
+  static void init_counters();
+  static void print_counters_on(outputStream* st);
 };
 
 #endif // SHARE_OPTO_RUNTIME_HPP
diff a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp
--- a/src/hotspot/share/opto/type.cpp
+++ b/src/hotspot/share/opto/type.cpp
@@ -88,11 +88,11 @@
   { Bad,             T_ILLEGAL,    "vectorx:",      false, Op_VecX,              relocInfo::none          },  // VectorX
   { Bad,             T_ILLEGAL,    "vectory:",      false, Op_VecY,              relocInfo::none          },  // VectorY
   { Bad,             T_ILLEGAL,    "vectorz:",      false, Op_VecZ,              relocInfo::none          },  // VectorZ
 #endif
   { Bad,             T_ADDRESS,    "anyptr:",       false, Op_RegP,              relocInfo::none          },  // AnyPtr
-  { Bad,             T_ADDRESS,    "rawptr:",       false, Op_RegP,              relocInfo::none          },  // RawPtr
+  { Bad,             T_ADDRESS,    "rawptr:",       false, Op_RegP,              relocInfo::external_word_type },  // RawPtr
   { Bad,             T_OBJECT,     "oop:",          true,  Op_RegP,              relocInfo::oop_type      },  // OopPtr
   { Bad,             T_OBJECT,     "inst:",         true,  Op_RegP,              relocInfo::oop_type      },  // InstPtr
   { Bad,             T_OBJECT,     "ary:",          true,  Op_RegP,              relocInfo::oop_type      },  // AryPtr
   { Bad,             T_METADATA,   "metadata:",     false, Op_RegP,              relocInfo::metadata_type },  // MetadataPtr
   { Bad,             T_METADATA,   "klass:",        false, Op_RegP,              relocInfo::metadata_type },  // KlassPtr
@@ -4526,11 +4526,11 @@
   case Constant:
     if (WizardMode || Verbose) {
       ResourceMark rm;
       stringStream ss;
 
-      st->print(" ");
+      st->print(" " INTPTR_FORMAT, p2i(const_oop()));
       const_oop()->print_oop(&ss);
       // 'const_oop->print_oop()' may emit newlines('\n') into ss.
       // suppress newlines from it so -XX:+Verbose -XX:+PrintIdeal dumps one-liner for each node.
       char* buf = ss.as_string(/* c_heap= */false);
       StringUtils::replace_no_expand(buf, "\n", "");
@@ -5147,10 +5147,11 @@
   _ary->dump2(d,depth,st);
   _interfaces->dump(st);
 
   switch( _ptr ) {
   case Constant:
+    st->print(" " INTPTR_FORMAT " ", p2i(const_oop()));
     const_oop()->print(st);
     break;
   case BotPTR:
     if (!WizardMode && !Verbose) {
       if( _klass_is_exact ) st->print(":exact");
diff a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp
--- a/src/hotspot/share/opto/vectorIntrinsics.cpp
+++ b/src/hotspot/share/opto/vectorIntrinsics.cpp
@@ -321,12 +321,14 @@
   const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr();
   const TypeInstPtr* mask_klass   = gvn().type(argument(2))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(3))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(4))->isa_int();
 
-  if (opr == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
-      !opr->is_con() || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
+  if (opr          == nullptr || !opr->is_con() ||
+      vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con()) {
     log_if_needed("  ** missing constant: opr=%s vclass=%s etype=%s vlen=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(3)->Opcode()],
                     NodeClassNames[argument(4)->Opcode()]);
@@ -553,12 +555,14 @@
   const TypeInt*     start_val     = gvn().type(argument(4))->isa_int();
   const TypeInt*     step_val      = gvn().type(argument(5))->isa_int();
   const TypeInt*     wrap          = gvn().type(argument(6))->isa_int();
 
   if (shuffle_klass == nullptr || shuffle_klass->const_oop() == nullptr ||
-      vlen == nullptr || !vlen->is_con() || start_val == nullptr || step_val == nullptr ||
-      wrap == nullptr || !wrap->is_con()) {
+      vlen          == nullptr || !vlen->is_con() ||
+      start_val     == nullptr ||
+      step_val      == nullptr ||
+      wrap          == nullptr || !wrap->is_con()) {
     return false; // not enough info for intrinsification
   }
 
   if (!is_klass_initialized(shuffle_klass)) {
     log_if_needed("  ** klass argument not initialized");
@@ -651,11 +655,15 @@
   const TypeInstPtr* mask_klass = gvn().type(argument(1))->isa_instptr();
   const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr();
   const TypeInt*     vlen       = gvn().type(argument(3))->isa_int();
   Node*              mask       = argument(4);
 
-  if (mask_klass == nullptr || elem_klass == nullptr || mask->is_top() || vlen == nullptr) {
+  if (mask_klass == nullptr || mask_klass->const_oop() == nullptr ||
+      elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
+      vlen       == nullptr || !vlen->is_con() ||
+      oper       == nullptr || !oper->is_con() ||
+      mask->is_top()) {
     return false; // dead code
   }
 
   if (!is_klass_initialized(mask_klass)) {
     log_if_needed("  ** klass argument not initialized");
@@ -712,11 +720,11 @@
   const TypeInt*     vlen          = gvn().type(argument(4))->isa_int();
 
   if (vector_klass == nullptr || elem_klass == nullptr || shuffle_klass == nullptr || shuffle->is_top() || vlen == nullptr) {
     return false; // dead code
   }
-  if (!vlen->is_con() || vector_klass->const_oop() == nullptr || shuffle_klass->const_oop() == nullptr) {
+  if (vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || shuffle_klass->const_oop() == nullptr || !vlen->is_con()) {
     return false; // not enough info for intrinsification
   }
   if (!is_klass_initialized(shuffle_klass) || !is_klass_initialized(vector_klass) ) {
     log_if_needed("  ** klass argument not initialized");
     return false;
@@ -830,13 +838,15 @@
   // Mode argument determines the mode of operation it can take following values:-
   // MODE_BROADCAST for vector Vector.broadcast and VectorMask.maskAll operations.
   // MODE_BITS_COERCED_LONG_TO_MASK for VectorMask.fromLong operation.
   const TypeInt*     mode         = gvn().type(argument(5))->isa_int();
 
-  if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || mode == nullptr ||
-      bits_type == nullptr || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr ||
-      !vlen->is_con() || !mode->is_con()) {
+  if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con() ||
+      bits_type    == nullptr ||
+      mode         == nullptr || !mode->is_con()) {
     log_if_needed("  ** missing constant: vclass=%s etype=%s vlen=%s bitwise=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()],
                     NodeClassNames[argument(5)->Opcode()]);
@@ -958,12 +968,14 @@
   const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(1))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(2))->isa_int();
   const TypeInt*     from_ms      = gvn().type(argument(6))->isa_int();
 
-  if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || !from_ms->is_con() ||
-      vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
+  if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con() ||
+      from_ms      == nullptr || !from_ms->is_con()) {
     log_if_needed("  ** missing constant: vclass=%s etype=%s vlen=%s from_ms=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()],
                     NodeClassNames[argument(6)->Opcode()]);
@@ -1166,13 +1178,15 @@
   const TypeInstPtr* mask_klass   = gvn().type(argument(1))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(2))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(3))->isa_int();
   const TypeInt*     from_ms      = gvn().type(argument(7))->isa_int();
 
-  if (vector_klass == nullptr || mask_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
-      vector_klass->const_oop() == nullptr || mask_klass->const_oop() == nullptr || from_ms == nullptr ||
-      elem_klass->const_oop() == nullptr || !vlen->is_con() || !from_ms->is_con()) {
+  if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+      mask_klass   == nullptr || mask_klass->const_oop()   == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con() ||
+      from_ms      == nullptr || !from_ms->is_con()) {
     log_if_needed("  ** missing constant: vclass=%s mclass=%s etype=%s vlen=%s from_ms=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()],
                     NodeClassNames[argument(3)->Opcode()],
@@ -1405,12 +1419,15 @@
   const TypeInstPtr* mask_klass       = gvn().type(argument(1))->isa_instptr();
   const TypeInstPtr* elem_klass       = gvn().type(argument(2))->isa_instptr();
   const TypeInt*     vlen             = gvn().type(argument(3))->isa_int();
   const TypeInstPtr* vector_idx_klass = gvn().type(argument(4))->isa_instptr();
 
-  if (vector_klass == nullptr || elem_klass == nullptr || vector_idx_klass == nullptr || vlen == nullptr ||
-      vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || vector_idx_klass->const_oop() == nullptr || !vlen->is_con()) {
+  if (vector_klass     == nullptr || vector_klass->const_oop()     == nullptr ||
+//      mask_klass       == nullptr || mask_klass->const_oop()       == nullptr ||
+      elem_klass       == nullptr || elem_klass->const_oop()       == nullptr ||
+      vlen             == nullptr || !vlen->is_con() ||
+      vector_idx_klass == nullptr || vector_idx_klass->const_oop() == nullptr) {
     log_if_needed("  ** missing constant: vclass=%s etype=%s vlen=%s viclass=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()],
                     NodeClassNames[argument(3)->Opcode()],
                     NodeClassNames[argument(4)->Opcode()]);
@@ -1592,12 +1609,15 @@
   const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr();
   const TypeInstPtr* mask_klass   = gvn().type(argument(2))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(3))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(4))->isa_int();
 
-  if (opr == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
-      !opr->is_con() || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
+  if (opr          == nullptr || !opr->is_con() ||
+      vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+//      mask_klass   == nullptr || mask_klass->const_oop()   == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con()) {
     log_if_needed("  ** missing constant: opr=%s vclass=%s etype=%s vlen=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(3)->Opcode()],
                     NodeClassNames[argument(4)->Opcode()]);
@@ -1729,12 +1749,14 @@
   const TypeInt*     cond         = gvn().type(argument(0))->isa_int();
   const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(2))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(3))->isa_int();
 
-  if (cond == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
-      !cond->is_con() || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
+  if (cond         == nullptr || !cond->is_con() ||
+      vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con()) {
     log_if_needed("  ** missing constant: cond=%s vclass=%s etype=%s vlen=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()],
                     NodeClassNames[argument(3)->Opcode()]);
@@ -2741,11 +2763,11 @@
   const TypeInt*     idx          = gvn().type(argument(4))->isa_int();
 
   if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || idx == nullptr) {
     return false; // dead code
   }
-  if (vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
+  if (vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con() || !idx->is_con()) {
     log_if_needed("  ** missing constant: vclass=%s etype=%s vlen=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()]);
     return false; // not enough info for intrinsification
@@ -3040,13 +3062,15 @@
   const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr();
   const TypeInstPtr* mask_klass   = gvn().type(argument(2))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(3))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(4))->isa_int();
 
-  if (vector_klass == nullptr || elem_klass == nullptr || mask_klass == nullptr || vlen == nullptr ||
-      vector_klass->const_oop() == nullptr || mask_klass->const_oop() == nullptr ||
-      elem_klass->const_oop() == nullptr || !vlen->is_con() || !opr->is_con()) {
+  if (opr          == nullptr || !opr->is_con() ||
+      vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+      mask_klass   == nullptr || mask_klass->const_oop()   == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con()) {
     log_if_needed("  ** missing constant: opr=%s vclass=%s mclass=%s etype=%s vlen=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()],
                     NodeClassNames[argument(3)->Opcode()],
@@ -3121,13 +3145,13 @@
 bool LibraryCallKit::inline_index_vector() {
   const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(1))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(2))->isa_int();
 
-  if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
-      vector_klass->const_oop() == nullptr || !vlen->is_con() ||
-      elem_klass->const_oop() == nullptr) {
+  if (vector_klass == nullptr || vector_klass->const_oop() == nullptr ||
+      elem_klass   == nullptr || elem_klass->const_oop()   == nullptr ||
+      vlen         == nullptr || !vlen->is_con() ) {
     log_if_needed("  ** missing constant: vclass=%s etype=%s vlen=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()]);
     return false; // not enough info for intrinsification
@@ -3255,12 +3279,13 @@
 bool LibraryCallKit::inline_index_partially_in_upper_range() {
   const TypeInstPtr* mask_klass   = gvn().type(argument(0))->isa_instptr();
   const TypeInstPtr* elem_klass   = gvn().type(argument(1))->isa_instptr();
   const TypeInt*     vlen         = gvn().type(argument(2))->isa_int();
 
-  if (mask_klass == nullptr || elem_klass == nullptr || vlen == nullptr ||
-      mask_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) {
+  if (mask_klass == nullptr || mask_klass->const_oop() == nullptr ||
+      elem_klass == nullptr || elem_klass->const_oop() == nullptr ||
+      vlen       == nullptr || !vlen->is_con()) {
     log_if_needed("  ** missing constant: mclass=%s etype=%s vlen=%s",
                     NodeClassNames[argument(0)->Opcode()],
                     NodeClassNames[argument(1)->Opcode()],
                     NodeClassNames[argument(2)->Opcode()]);
     return false; // not enough info for intrinsification
diff a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp
--- a/src/hotspot/share/prims/jni.cpp
+++ b/src/hotspot/share/prims/jni.cpp
@@ -969,11 +969,11 @@
   HOTSPOT_JNI_ALLOCOBJECT_ENTRY(env, clazz);
 
   jobject ret = nullptr;
   DT_RETURN_MARK(AllocObject, jobject, (const jobject&)ret);
 
-  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), CHECK_NULL);
+  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), "jni", CHECK_NULL);
   ret = JNIHandles::make_local(THREAD, i);
   return ret;
 JNI_END
 
 DT_RETURN_MARK_DECL(NewObjectA, jobject
@@ -983,11 +983,11 @@
   HOTSPOT_JNI_NEWOBJECTA_ENTRY(env, clazz, (uintptr_t) methodID);
 
   jobject obj = nullptr;
   DT_RETURN_MARK(NewObjectA, jobject, (const jobject&)obj);
 
-  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), CHECK_NULL);
+  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), "jni", CHECK_NULL);
   obj = JNIHandles::make_local(THREAD, i);
   JavaValue jvalue(T_VOID);
   JNI_ArgumentPusherArray ap(methodID, args);
   jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_NULL);
   return obj;
@@ -1001,11 +1001,11 @@
   HOTSPOT_JNI_NEWOBJECTV_ENTRY(env, clazz, (uintptr_t) methodID);
 
   jobject obj = nullptr;
   DT_RETURN_MARK(NewObjectV, jobject, (const jobject&)obj);
 
-  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), CHECK_NULL);
+  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), "jni", CHECK_NULL);
   obj = JNIHandles::make_local(THREAD, i);
   JavaValue jvalue(T_VOID);
   JNI_ArgumentPusherVaArg ap(methodID, args);
   jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_NULL);
   return obj;
@@ -1019,11 +1019,11 @@
   HOTSPOT_JNI_NEWOBJECT_ENTRY(env, clazz, (uintptr_t) methodID);
 
   jobject obj = nullptr;
   DT_RETURN_MARK(NewObject, jobject, (const jobject&)obj);
 
-  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), CHECK_NULL);
+  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(clazz), "jni", CHECK_NULL);
   obj = JNIHandles::make_local(THREAD, i);
   va_list args;
   va_start(args, methodID);
   JavaValue jvalue(T_VOID);
   JNI_ArgumentPusherVaArg ap(methodID, args);
diff a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotConstantPoolResolver.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/classListParser.hpp"
 #include "cds/classListWriter.hpp"
 #include "cds/dynamicArchive.hpp"
 #include "cds/heapShared.hpp"
@@ -83,11 +84,11 @@
 #include "runtime/javaThread.hpp"
 #include "runtime/jfieldIDWorkaround.hpp"
 #include "runtime/jniHandles.inline.hpp"
 #include "runtime/os.inline.hpp"
 #include "runtime/osThread.hpp"
-#include "runtime/perfData.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/reflection.hpp"
 #include "runtime/synchronizer.hpp"
 #include "runtime/threadIdentifier.hpp"
 #include "runtime/threadSMR.hpp"
 #include "runtime/vframe.inline.hpp"
@@ -241,26 +242,42 @@
 }
 
 // java.lang.System //////////////////////////////////////////////////////////////////////
 
 
-JVM_LEAF(jlong, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored))
+JVM_LEAF_PROF(jboolean, JVM_AOTIsTraining, JVM_AOTIsTraining(JNIEnv *env))
+#if INCLUDE_CDS
+  return MetaspaceShared::is_recording_preimage_static_archive();
+#else
+  return JNI_FALSE;
+#endif // INCLUDE_CDS
+JVM_END
+
+JVM_ENTRY_PROF(void, JVM_AOTEndTraining, JVM_AOTEndTraining(JNIEnv *env))
+#if INCLUDE_CDS
+  if (MetaspaceShared::is_recording_preimage_static_archive()) {
+    MetaspaceShared::preload_and_dump(THREAD);
+  }
+#endif // INCLUDE_CDS
+JVM_END
+
+JVM_LEAF_PROF(jlong, JVM_CurrentTimeMillis, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored))
   return os::javaTimeMillis();
 JVM_END
 
-JVM_LEAF(jlong, JVM_NanoTime(JNIEnv *env, jclass ignored))
+JVM_LEAF_PROF(jlong, JVM_NanoTime, JVM_NanoTime(JNIEnv *env, jclass ignored))
   return os::javaTimeNanos();
 JVM_END
 
 // The function below is actually exposed by jdk.internal.misc.VM and not
 // java.lang.System, but we choose to keep it here so that it stays next
 // to JVM_CurrentTimeMillis and JVM_NanoTime
 
 const jlong MAX_DIFF_SECS = CONST64(0x0100000000); //  2^32
 const jlong MIN_DIFF_SECS = -MAX_DIFF_SECS; // -2^32
 
-JVM_LEAF(jlong, JVM_GetNanoTimeAdjustment(JNIEnv *env, jclass ignored, jlong offset_secs))
+JVM_LEAF_PROF(jlong, JVM_GetNanoTimeAdjustment, JVM_GetNanoTimeAdjustment(JNIEnv *env, jclass ignored, jlong offset_secs))
   jlong seconds;
   jlong nanos;
 
   os::javaTimeSystemUTC(seconds, nanos);
 
@@ -291,11 +308,11 @@
   // this number of nanoseconds along with the number of seconds
   // in the offset you should get the current UTC time.
   return (diff * (jlong)1000000000) + nanos;
 JVM_END
 
-JVM_ENTRY(void, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos,
+JVM_ENTRY_PROF(void, JVM_ArrayCopy, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos,
                                jobject dst, jint dst_pos, jint length))
   // Check if we have null pointers
   if (src == nullptr || dst == nullptr) {
     THROW(vmSymbols::java_lang_NullPointerException());
   }
@@ -330,11 +347,11 @@
 /*
  * Return all of the system properties in a Java String array with alternating
  * names and values from the jvm SystemProperty.
  * Which includes some internal and all commandline -D defined properties.
  */
-JVM_ENTRY(jobjectArray, JVM_GetProperties(JNIEnv *env))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetProperties, JVM_GetProperties(JNIEnv *env))
   ResourceMark rm(THREAD);
   HandleMark hm(THREAD);
   int ndx = 0;
   int fixedCount = 2;
 
@@ -418,11 +435,11 @@
  *
  * It is important that this directory is well-known and the
  * same for all VM instances. It cannot be affected by configuration
  * variables such as java.io.tmpdir.
  */
-JVM_ENTRY(jstring, JVM_GetTemporaryDirectory(JNIEnv *env))
+JVM_ENTRY_PROF(jstring, JVM_GetTemporaryDirectory, JVM_GetTemporaryDirectory(JNIEnv *env))
   HandleMark hm(THREAD);
   const char* temp_dir = os::get_temp_directory();
   Handle h = java_lang_String::create_from_platform_dependent_str(temp_dir, CHECK_NULL);
   return (jstring) JNIHandles::make_local(THREAD, h());
 JVM_END
@@ -430,69 +447,69 @@
 
 // java.lang.Runtime /////////////////////////////////////////////////////////////////////////
 
 extern volatile jint vm_created;
 
-JVM_ENTRY_NO_ENV(void, JVM_BeforeHalt())
+JVM_ENTRY_NO_ENV_PROF(void, JVM_BeforeHalt, JVM_BeforeHalt())
   EventShutdown event;
   if (event.should_commit()) {
     event.set_reason("Shutdown requested from Java");
     event.commit();
   }
 JVM_END
 
 
-JVM_ENTRY_NO_ENV(void, JVM_Halt(jint code))
+JVM_ENTRY_NO_ENV_PROF(void, JVM_Halt, JVM_Halt(jint code))
   before_exit(thread, true);
   vm_exit(code);
 JVM_END
 
 
-JVM_ENTRY_NO_ENV(void, JVM_GC(void))
+JVM_ENTRY_NO_ENV_PROF(void, JVM_GC, JVM_GC(void))
   if (!DisableExplicitGC) {
     EventSystemGC event;
     event.set_invokedConcurrent(ExplicitGCInvokesConcurrent);
     Universe::heap()->collect(GCCause::_java_lang_system_gc);
     event.commit();
   }
 JVM_END
 
 
-JVM_LEAF(jlong, JVM_MaxObjectInspectionAge(void))
+JVM_LEAF_PROF(jlong, JVM_MaxObjectInspectionAge, JVM_MaxObjectInspectionAge(void))
   return Universe::heap()->millis_since_last_whole_heap_examined();
 JVM_END
 
 
 static inline jlong convert_size_t_to_jlong(size_t val) {
   // In the 64-bit vm, a size_t can overflow a jlong (which is signed).
   NOT_LP64 (return (jlong)val;)
   LP64_ONLY(return (jlong)MIN2(val, (size_t)max_jlong);)
 }
 
-JVM_ENTRY_NO_ENV(jlong, JVM_TotalMemory(void))
+JVM_ENTRY_NO_ENV_PROF(jlong, JVM_TotalMemory, JVM_TotalMemory(void))
   size_t n = Universe::heap()->capacity();
   return convert_size_t_to_jlong(n);
 JVM_END
 
 
-JVM_ENTRY_NO_ENV(jlong, JVM_FreeMemory(void))
+JVM_ENTRY_NO_ENV_PROF(jlong, JVM_FreeMemory, JVM_FreeMemory(void))
   size_t n = Universe::heap()->unused();
   return convert_size_t_to_jlong(n);
 JVM_END
 
 
-JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void))
+JVM_ENTRY_NO_ENV_PROF(jlong, JVM_MaxMemory, JVM_MaxMemory(void))
   size_t n = Universe::heap()->max_capacity();
   return convert_size_t_to_jlong(n);
 JVM_END
 
 
-JVM_ENTRY_NO_ENV(jint, JVM_ActiveProcessorCount(void))
+JVM_ENTRY_NO_ENV_PROF(jint, JVM_ActiveProcessorCount, JVM_ActiveProcessorCount(void))
   return os::active_processor_count();
 JVM_END
 
-JVM_LEAF(jboolean, JVM_IsUseContainerSupport(void))
+JVM_LEAF_PROF(jboolean, JVM_IsUseContainerSupport, JVM_IsUseContainerSupport(void))
 #ifdef LINUX
   if (UseContainerSupport) {
     return JNI_TRUE;
   }
 #endif
@@ -508,18 +525,18 @@
   return JNI_FALSE;
 JVM_END
 
 // java.lang.Throwable //////////////////////////////////////////////////////
 
-JVM_ENTRY(void, JVM_FillInStackTrace(JNIEnv *env, jobject receiver))
+JVM_ENTRY_PROF(void, JVM_FillInStackTrace, JVM_FillInStackTrace(JNIEnv *env, jobject receiver))
   Handle exception(thread, JNIHandles::resolve_non_null(receiver));
   java_lang_Throwable::fill_in_stack_trace(exception);
 JVM_END
 
 // java.lang.NullPointerException ///////////////////////////////////////////
 
-JVM_ENTRY(jstring, JVM_GetExtendedNPEMessage(JNIEnv *env, jthrowable throwable))
+JVM_ENTRY_PROF(jstring, JVM_GetExtendedNPEMessage, JVM_GetExtendedNPEMessage(JNIEnv *env, jthrowable throwable))
   if (!ShowCodeDetailsInExceptionMessages) return nullptr;
 
   oop exc = JNIHandles::resolve_non_null(throwable);
 
   Method* method;
@@ -542,28 +559,28 @@
 JVM_END
 
 // java.lang.StackTraceElement //////////////////////////////////////////////
 
 
-JVM_ENTRY(void, JVM_InitStackTraceElementArray(JNIEnv *env, jobjectArray elements, jobject backtrace, jint depth))
+JVM_ENTRY_PROF(void, JVM_InitStackTraceElementArray, JVM_InitStackTraceElementArray(JNIEnv *env, jobjectArray elements, jobject backtrace, jint depth))
   Handle backtraceh(THREAD, JNIHandles::resolve(backtrace));
   objArrayOop st = objArrayOop(JNIHandles::resolve(elements));
   objArrayHandle stack_trace(THREAD, st);
   // Fill in the allocated stack trace
   java_lang_Throwable::get_stack_trace_elements(depth, backtraceh, stack_trace, CHECK);
 JVM_END
 
 
-JVM_ENTRY(void, JVM_InitStackTraceElement(JNIEnv* env, jobject element, jobject stackFrameInfo))
+JVM_ENTRY_PROF(void, JVM_InitStackTraceElement, JVM_InitStackTraceElement(JNIEnv* env, jobject element, jobject stackFrameInfo))
   Handle stack_frame_info(THREAD, JNIHandles::resolve_non_null(stackFrameInfo));
   Handle stack_trace_element(THREAD, JNIHandles::resolve_non_null(element));
   java_lang_StackFrameInfo::to_stack_trace_element(stack_frame_info, stack_trace_element, CHECK);
 JVM_END
 
 
 // java.lang.StackWalker //////////////////////////////////////////////////////
-JVM_ENTRY(void, JVM_ExpandStackFrameInfo(JNIEnv *env, jobject obj))
+JVM_ENTRY_PROF(void, JVM_ExpandStackFrameInfo, JVM_ExpandStackFrameInfo(JNIEnv *env, jobject obj))
   Handle stack_frame_info(THREAD, JNIHandles::resolve_non_null(obj));
 
   bool have_name = (java_lang_StackFrameInfo::name(stack_frame_info()) != nullptr);
   bool have_type = (java_lang_StackFrameInfo::type(stack_frame_info()) != nullptr);
   Method* method = java_lang_StackFrameInfo::get_method(stack_frame_info());
@@ -575,13 +592,13 @@
     Handle type = java_lang_String::create_from_symbol(method->signature(), CHECK);
     java_lang_StackFrameInfo::set_type(stack_frame_info(), type());
   }
 JVM_END
 
-JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jint mode,
-                                     jint skip_frames, jobject contScope, jobject cont,
-                                     jint buffer_size, jint start_index, jobjectArray frames))
+JVM_ENTRY_PROF(jobject, JVM_CallStackWalk, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jint mode,
+                                                         jint skip_frames, jobject contScope, jobject cont,
+                                                         jint buffer_size, jint start_index, jobjectArray frames))
   if (!thread->has_last_Java_frame()) {
     THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: no stack trace", nullptr);
   }
 
   Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
@@ -601,13 +618,13 @@
                                buffer_size, start_index, frames_array_h, CHECK_NULL);
   return JNIHandles::make_local(THREAD, result);
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jint mode, jlong anchor,
-                                  jint last_batch_count, jint buffer_size, jint start_index,
-                                  jobjectArray frames))
+JVM_ENTRY_PROF(jint, JVM_MoreStackWalk, JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jint mode, jlong anchor,
+                                                      jint last_batch_count, jint buffer_size, jint start_index,
+                                                      jobjectArray frames))
   // frames array is a ClassFrameInfo[] array when only getting caller reference,
   // and a StackFrameInfo[] array (or derivative) otherwise. It should never
   // be null.
   objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
   objArrayHandle frames_array_h(THREAD, fa);
@@ -619,11 +636,11 @@
   Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
   return StackWalk::fetchNextBatch(stackStream_h, mode, anchor, last_batch_count, buffer_size,
                                   start_index, frames_array_h, THREAD);
 JVM_END
 
-JVM_ENTRY(void, JVM_SetStackWalkContinuation(JNIEnv *env, jobject stackStream, jlong anchor, jobjectArray frames, jobject cont))
+JVM_ENTRY_PROF(void, JVM_SetStackWalkContinuation, JVM_SetStackWalkContinuation(JNIEnv *env, jobject stackStream, jlong anchor, jobjectArray frames, jobject cont))
   objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
   objArrayHandle frames_array_h(THREAD, fa);
   Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
   Handle cont_h(THREAD, JNIHandles::resolve_non_null(cont));
 
@@ -631,18 +648,18 @@
 JVM_END
 
 // java.lang.Object ///////////////////////////////////////////////
 
 
-JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
+JVM_ENTRY_PROF(jint, JVM_IHashCode, JVM_IHashCode(JNIEnv* env, jobject handle))
   // as implemented in the classic virtual machine; return 0 if object is null
   return handle == nullptr ? 0 :
          checked_cast<jint>(ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)));
 JVM_END
 
 
-JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
+JVM_ENTRY_PROF(void, JVM_MonitorWait, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
   Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
   JavaThreadInObjectWaitState jtiows(thread, ms != 0);
   if (JvmtiExport::should_post_monitor_wait()) {
     JvmtiExport::post_monitor_wait(thread, obj(), ms);
 
@@ -654,23 +671,23 @@
   }
   ObjectSynchronizer::wait(obj, ms, CHECK);
 JVM_END
 
 
-JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
+JVM_ENTRY_PROF(void, JVM_MonitorNotify, JVM_MonitorNotify(JNIEnv* env, jobject handle))
   Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
   ObjectSynchronizer::notify(obj, CHECK);
 JVM_END
 
 
-JVM_ENTRY(void, JVM_MonitorNotifyAll(JNIEnv* env, jobject handle))
+JVM_ENTRY_PROF(void, JVM_MonitorNotifyAll, JVM_MonitorNotifyAll(JNIEnv* env, jobject handle))
   Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
   ObjectSynchronizer::notifyall(obj, CHECK);
 JVM_END
 
 
-JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
+JVM_ENTRY_PROF(jobject, JVM_Clone, JVM_Clone(JNIEnv* env, jobject handle))
   Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
   Klass* klass = obj->klass();
   JvmtiVMObjectAllocEventCollector oam;
 
 #ifdef ASSERT
@@ -719,35 +736,35 @@
   return JNIHandles::make_local(THREAD, new_obj());
 JVM_END
 
 // java.lang.ref.Finalizer ////////////////////////////////////////////////////
 
-JVM_ENTRY(void, JVM_ReportFinalizationComplete(JNIEnv * env, jobject finalizee))
+JVM_ENTRY_PROF(void, JVM_ReportFinalizationComplete, JVM_ReportFinalizationComplete(JNIEnv * env, jobject finalizee))
   MANAGEMENT_ONLY(FinalizerService::on_complete(JNIHandles::resolve_non_null(finalizee), THREAD);)
 JVM_END
 
-JVM_LEAF(jboolean, JVM_IsFinalizationEnabled(JNIEnv * env))
+JVM_LEAF_PROF(jboolean, JVM_IsFinalizationEnabled, JVM_IsFinalizationEnabled(JNIEnv * env))
   return InstanceKlass::is_finalization_enabled();
 JVM_END
 
 // jdk.internal.vm.Continuation /////////////////////////////////////////////////////
 
-JVM_ENTRY(void, JVM_RegisterContinuationMethods(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(void, JVM_RegisterContinuationMethods, JVM_RegisterContinuationMethods(JNIEnv *env, jclass cls))
   CONT_RegisterNativeMethods(env, cls);
 JVM_END
 
 // java.io.File ///////////////////////////////////////////////////////////////
 
-JVM_LEAF(char*, JVM_NativePath(char* path))
+JVM_LEAF_PROF(char*, JVM_NativePath, JVM_NativePath(char* path))
   return os::native_path(path);
 JVM_END
 
 
 // Misc. class handling ///////////////////////////////////////////////////////////
 
 
-JVM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env))
+JVM_ENTRY_PROF(jclass, JVM_GetCallerClass, JVM_GetCallerClass(JNIEnv* env))
   // Getting the class of the caller frame.
   //
   // The call stack at this point looks something like this:
   //
   // [0] [ @CallerSensitive public jdk.internal.reflect.Reflection.getCallerClass ]
@@ -782,11 +799,11 @@
   }
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(jclass, JVM_FindPrimitiveClass(JNIEnv* env, const char* utf))
+JVM_ENTRY_PROF(jclass, JVM_FindPrimitiveClass, JVM_FindPrimitiveClass(JNIEnv* env, const char* utf))
   oop mirror = nullptr;
   BasicType t = name2type(utf);
   if (t != T_ILLEGAL && !is_reference_type(t)) {
     mirror = Universe::java_mirror(t);
   }
@@ -799,12 +816,11 @@
 
 
 // Returns a class loaded by the bootstrap class loader; or null
 // if not found.  ClassNotFoundException is not thrown.
 // FindClassFromBootLoader is exported to the launcher for windows.
-JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
-                                              const char* name))
+JVM_ENTRY_PROF(jclass, JVM_FindClassFromBootLoader, JVM_FindClassFromBootLoader(JNIEnv* env, const char* name))
   // Java libraries should ensure that name is never null or illegal.
   if (name == nullptr || (int)strlen(name) > Symbol::max_length()) {
     // It's impossible to create this class;  the name cannot fit
     // into the constant pool.
     return nullptr;
@@ -822,13 +838,13 @@
   }
   return (jclass) JNIHandles::make_local(THREAD, k->java_mirror());
 JVM_END
 
 // Find a class with this name in this loader, using the caller's protection domain.
-JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name,
-                                          jboolean init, jobject loader,
-                                          jclass caller))
+JVM_ENTRY_PROF(jclass, JVM_FindClassFromCaller, JVM_FindClassFromCaller(JNIEnv* env, const char* name,
+                                                                   jboolean init, jobject loader,
+                                                                   jclass caller))
   TempNewSymbol h_name =
        SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_ClassNotFoundException(),
                                            CHECK_NULL);
 
   oop loader_oop = JNIHandles::resolve(loader);
@@ -853,12 +869,12 @@
   }
   return result;
 JVM_END
 
 // Currently only called from the old verifier.
-JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
-                                         jboolean init, jclass from))
+JVM_ENTRY_PROF(jclass, JVM_FindClassFromClass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
+                                                                 jboolean init, jclass from))
   TempNewSymbol h_name =
        SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_ClassNotFoundException(),
                                            CHECK_NULL);
   oop from_class_oop = JNIHandles::resolve(from);
   Klass* from_class = (from_class_oop == nullptr)
@@ -1057,11 +1073,11 @@
   }
 
   return (jclass) JNIHandles::make_local(THREAD, ik->java_mirror());
 }
 
-JVM_ENTRY(jclass, JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd))
+JVM_ENTRY_PROF(jclass, JVM_DefineClass, JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd))
   return jvm_define_class_common(name, loader, buf, len, pd, nullptr, THREAD);
 JVM_END
 
 /*
  * Define a class with the specified lookup class.
@@ -1072,11 +1088,11 @@
  *  pd:      protection domain
  *  init:    initialize the class
  *  flags:   properties of the class
  *  classData: private static pre-initialized field
  */
-JVM_ENTRY(jclass, JVM_LookupDefineClass(JNIEnv *env, jclass lookup, const char *name, const jbyte *buf,
+JVM_ENTRY_PROF(jclass, JVM_LookupDefineClass, JVM_LookupDefineClass(JNIEnv *env, jclass lookup, const char *name, const jbyte *buf,
           jsize len, jobject pd, jboolean initialize, int flags, jobject classData))
 
   if (lookup == nullptr) {
     THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null");
   }
@@ -1084,16 +1100,15 @@
   assert(buf != nullptr, "buf must not be null");
 
   return jvm_lookup_define_class(lookup, name, buf, len, pd, initialize, flags, classData, THREAD);
 JVM_END
 
-JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source))
-
+JVM_ENTRY_PROF(jclass, JVM_DefineClassWithSource, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source))
   return jvm_define_class_common(name, loader, buf, len, pd, source, THREAD);
 JVM_END
 
-JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
+JVM_ENTRY_PROF(jclass, JVM_FindLoadedClass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
   ResourceMark rm(THREAD);
 
   Handle h_name (THREAD, JNIHandles::resolve_non_null(name));
   char* str = java_lang_String::as_utf8_string(h_name());
 
@@ -1135,63 +1150,63 @@
             (jclass) JNIHandles::make_local(THREAD, k->java_mirror());
 JVM_END
 
 // Module support //////////////////////////////////////////////////////////////////////////////
 
-JVM_ENTRY(void, JVM_DefineModule(JNIEnv *env, jobject module, jboolean is_open, jstring version,
-                                 jstring location, jobjectArray packages))
+JVM_ENTRY_PROF(void, JVM_DefineModule, JVM_DefineModule(JNIEnv *env, jobject module, jboolean is_open, jstring version,
+                                                   jstring location, jobjectArray packages))
   Handle h_module (THREAD, JNIHandles::resolve(module));
   Modules::define_module(h_module, is_open, version, location, packages, CHECK);
 JVM_END
 
-JVM_ENTRY(void, JVM_SetBootLoaderUnnamedModule(JNIEnv *env, jobject module))
+JVM_ENTRY_PROF(void, JVM_SetBootLoaderUnnamedModule, JVM_SetBootLoaderUnnamedModule(JNIEnv *env, jobject module))
   Handle h_module (THREAD, JNIHandles::resolve(module));
   Modules::set_bootloader_unnamed_module(h_module, CHECK);
 JVM_END
 
-JVM_ENTRY(void, JVM_AddModuleExports(JNIEnv *env, jobject from_module, jstring package, jobject to_module))
+JVM_ENTRY_PROF(void, JVM_AddModuleExports, JVM_AddModuleExports(JNIEnv *env, jobject from_module, jstring package, jobject to_module))
   Handle h_from_module (THREAD, JNIHandles::resolve(from_module));
   Handle h_to_module (THREAD, JNIHandles::resolve(to_module));
   Modules::add_module_exports_qualified(h_from_module, package, h_to_module, CHECK);
 JVM_END
 
-JVM_ENTRY(void, JVM_AddModuleExportsToAllUnnamed(JNIEnv *env, jobject from_module, jstring package))
+JVM_ENTRY_PROF(void, JVM_AddModuleExportsToAllUnnamed, JVM_AddModuleExportsToAllUnnamed(JNIEnv *env, jobject from_module, jstring package))
   Handle h_from_module (THREAD, JNIHandles::resolve(from_module));
   Modules::add_module_exports_to_all_unnamed(h_from_module, package, CHECK);
 JVM_END
 
-JVM_ENTRY(void, JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, jstring package))
+JVM_ENTRY_PROF(void, JVM_AddModuleExportsToAll, JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, jstring package))
   Handle h_from_module (THREAD, JNIHandles::resolve(from_module));
   Modules::add_module_exports(h_from_module, package, Handle(), CHECK);
 JVM_END
 
-JVM_ENTRY (void, JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject source_module))
+JVM_ENTRY_PROF(void, JVM_AddReadsModule, JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject source_module))
   Handle h_from_module (THREAD, JNIHandles::resolve(from_module));
   Handle h_source_module (THREAD, JNIHandles::resolve(source_module));
   Modules::add_reads_module(h_from_module, h_source_module, CHECK);
 JVM_END
 
-JVM_ENTRY(void, JVM_DefineArchivedModules(JNIEnv *env, jobject platform_loader, jobject system_loader))
+JVM_ENTRY_PROF(void, JVM_DefineArchivedModules, JVM_DefineArchivedModules(JNIEnv *env, jobject platform_loader, jobject system_loader))
   Handle h_platform_loader (THREAD, JNIHandles::resolve(platform_loader));
   Handle h_system_loader (THREAD, JNIHandles::resolve(system_loader));
   Modules::define_archived_modules(h_platform_loader, h_system_loader, CHECK);
 JVM_END
 
 // Reflection support //////////////////////////////////////////////////////////////////////////////
 
-JVM_ENTRY(jstring, JVM_InitClassName(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jstring, JVM_InitClassName, JVM_InitClassName(JNIEnv *env, jclass cls))
   assert (cls != nullptr, "illegal class");
   JvmtiVMObjectAllocEventCollector oam;
   ResourceMark rm(THREAD);
   HandleMark hm(THREAD);
   Handle java_class(THREAD, JNIHandles::resolve(cls));
   oop result = java_lang_Class::name(java_class, CHECK_NULL);
   return (jstring) JNIHandles::make_local(THREAD, result);
 JVM_END
 
 
-JVM_ENTRY(jobjectArray, JVM_GetClassInterfaces(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetClassInterfaces, JVM_GetClassInterfaces(JNIEnv *env, jclass cls))
   JvmtiVMObjectAllocEventCollector oam;
   oop mirror = JNIHandles::resolve_non_null(cls);
 
   // Special handling for primitive objects
   if (java_lang_Class::is_primitive(mirror)) {
@@ -1227,11 +1242,11 @@
   }
   return (jobjectArray) JNIHandles::make_local(THREAD, result());
 JVM_END
 
 
-JVM_ENTRY(jboolean, JVM_IsInterface(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jboolean, JVM_IsInterface, JVM_IsInterface(JNIEnv *env, jclass cls))
   oop mirror = JNIHandles::resolve_non_null(cls);
   if (java_lang_Class::is_primitive(mirror)) {
     return JNI_FALSE;
   }
   Klass* k = java_lang_Class::as_Klass(mirror);
@@ -1241,21 +1256,20 @@
   // The compiler intrinsic for isInterface tests the
   // Klass::_access_flags bits in the same way.
   return result;
 JVM_END
 
-JVM_ENTRY(jboolean, JVM_IsHiddenClass(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jboolean, JVM_IsHiddenClass, JVM_IsHiddenClass(JNIEnv *env, jclass cls))
   oop mirror = JNIHandles::resolve_non_null(cls);
   if (java_lang_Class::is_primitive(mirror)) {
     return JNI_FALSE;
   }
   Klass* k = java_lang_Class::as_Klass(mirror);
   return k->is_hidden();
 JVM_END
 
-
-JVM_ENTRY(jobject, JVM_GetProtectionDomain(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jobject, JVM_GetProtectionDomain, JVM_GetProtectionDomain(JNIEnv *env, jclass cls))
   oop mirror = JNIHandles::resolve_non_null(cls);
   if (mirror == nullptr) {
     THROW_(vmSymbols::java_lang_NullPointerException(), nullptr);
   }
 
@@ -1266,21 +1280,20 @@
 
   oop pd = java_lang_Class::protection_domain(mirror);
   return (jobject) JNIHandles::make_local(THREAD, pd);
 JVM_END
 
-
 class ScopedValueBindingsResolver {
 public:
   InstanceKlass* Carrier_klass;
   ScopedValueBindingsResolver(JavaThread* THREAD) {
     Klass *k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_ScopedValue_Carrier(), true, THREAD);
     Carrier_klass = InstanceKlass::cast(k);
   }
 };
 
-JVM_ENTRY(jobject, JVM_FindScopedValueBindings(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jobject, JVM_FindScopedValueBindings, JVM_FindScopedValueBindings(JNIEnv *env, jclass cls))
   ResourceMark rm(THREAD);
   GrowableArray<Handle>* local_array = new GrowableArray<Handle>(12);
   JvmtiVMObjectAllocEventCollector oam;
 
   static ScopedValueBindingsResolver resolver(THREAD);
@@ -1315,23 +1328,23 @@
   }
 
   return nullptr;
 JVM_END
 
-JVM_ENTRY(jboolean, JVM_IsArrayClass(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jboolean, JVM_IsArrayClass, JVM_IsArrayClass(JNIEnv *env, jclass cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   return (k != nullptr) && k->is_array_klass() ? true : false;
 JVM_END
 
 
-JVM_ENTRY(jboolean, JVM_IsPrimitiveClass(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jboolean, JVM_IsPrimitiveClass, JVM_IsPrimitiveClass(JNIEnv *env, jclass cls))
   oop mirror = JNIHandles::resolve_non_null(cls);
   return (jboolean) java_lang_Class::is_primitive(mirror);
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetClassModifiers(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jint, JVM_GetClassModifiers, JVM_GetClassModifiers(JNIEnv *env, jclass cls))
   oop mirror = JNIHandles::resolve_non_null(cls);
   if (java_lang_Class::is_primitive(mirror)) {
     // Primitive type
     return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC;
   }
@@ -1343,11 +1356,11 @@
 JVM_END
 
 
 // Inner class reflection ///////////////////////////////////////////////////////////////////////////////
 
-JVM_ENTRY(jobjectArray, JVM_GetDeclaredClasses(JNIEnv *env, jclass ofClass))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetDeclaredClasses, JVM_GetDeclaredClasses(JNIEnv *env, jclass ofClass))
   JvmtiVMObjectAllocEventCollector oam;
   // ofClass is a reference to a java_lang_Class object. The mirror object
   // of an InstanceKlass
   oop ofMirror = JNIHandles::resolve_non_null(ofClass);
   if (java_lang_Class::is_primitive(ofMirror) ||
@@ -1409,11 +1422,11 @@
 
   return (jobjectArray)JNIHandles::make_local(THREAD, result());
 JVM_END
 
 
-JVM_ENTRY(jclass, JVM_GetDeclaringClass(JNIEnv *env, jclass ofClass))
+JVM_ENTRY_PROF(jclass, JVM_GetDeclaringClass, JVM_GetDeclaringClass(JNIEnv *env, jclass ofClass))
 {
   // ofClass is a reference to a java_lang_Class object.
   oop ofMirror = JNIHandles::resolve_non_null(ofClass);
   if (java_lang_Class::is_primitive(ofMirror)) {
     return nullptr;
@@ -1430,11 +1443,11 @@
   if (!inner_is_member)  return nullptr;     // a hidden class (inside a method)
   return (jclass) JNIHandles::make_local(THREAD, outer_klass->java_mirror());
 }
 JVM_END
 
-JVM_ENTRY(jstring, JVM_GetSimpleBinaryName(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jstring, JVM_GetSimpleBinaryName, JVM_GetSimpleBinaryName(JNIEnv *env, jclass cls))
 {
   oop mirror = JNIHandles::resolve_non_null(cls);
   if (java_lang_Class::is_primitive(mirror)) {
     return nullptr;
   }
@@ -1454,11 +1467,11 @@
   }
   return nullptr;
 }
 JVM_END
 
-JVM_ENTRY(jstring, JVM_GetClassSignature(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jstring, JVM_GetClassSignature, JVM_GetClassSignature(JNIEnv *env, jclass cls))
   assert (cls != nullptr, "illegal class");
   JvmtiVMObjectAllocEventCollector oam;
   ResourceMark rm(THREAD);
   oop mirror = JNIHandles::resolve_non_null(cls);
   // Return null for arrays and primitives
@@ -1473,11 +1486,11 @@
   }
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(jbyteArray, JVM_GetClassAnnotations(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jbyteArray, JVM_GetClassAnnotations, JVM_GetClassAnnotations(JNIEnv *env, jclass cls))
   assert (cls != nullptr, "illegal class");
   oop mirror = JNIHandles::resolve_non_null(cls);
   // Return null for arrays and primitives
   if (!java_lang_Class::is_primitive(mirror)) {
     Klass* k = java_lang_Class::as_Klass(mirror);
@@ -1542,11 +1555,11 @@
   return m;  // caller has to deal with null in product mode
 }
 
 /* Type use annotations support (JDK 1.8) */
 
-JVM_ENTRY(jbyteArray, JVM_GetClassTypeAnnotations(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jbyteArray, JVM_GetClassTypeAnnotations, JVM_GetClassTypeAnnotations(JNIEnv *env, jclass cls))
   assert (cls != nullptr, "illegal class");
   ResourceMark rm(THREAD);
   // Return null for arrays and primitives
   if (!java_lang_Class::is_primitive(JNIHandles::resolve(cls))) {
     Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls));
@@ -1559,11 +1572,11 @@
     }
   }
   return nullptr;
 JVM_END
 
-JVM_ENTRY(jbyteArray, JVM_GetMethodTypeAnnotations(JNIEnv *env, jobject method))
+JVM_ENTRY_PROF(jbyteArray, JVM_GetMethodTypeAnnotations, JVM_GetMethodTypeAnnotations(JNIEnv *env, jobject method))
   assert (method != nullptr, "illegal method");
   // method is a handle to a java.lang.reflect.Method object
   Method* m = jvm_get_method_common(method);
   if (m == nullptr) {
     return nullptr;
@@ -1576,11 +1589,11 @@
   }
 
   return nullptr;
 JVM_END
 
-JVM_ENTRY(jbyteArray, JVM_GetFieldTypeAnnotations(JNIEnv *env, jobject field))
+JVM_ENTRY_PROF(jbyteArray, JVM_GetFieldTypeAnnotations, JVM_GetFieldTypeAnnotations(JNIEnv *env, jobject field))
   assert (field != nullptr, "illegal field");
   fieldDescriptor fd;
   bool gotFd = jvm_get_field_common(field, fd);
   if (!gotFd) {
     return nullptr;
@@ -1593,11 +1606,11 @@
   if (!cp->is_within_bounds(index)) {
     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Constant pool index out of bounds");
   }
 }
 
-JVM_ENTRY(jobjectArray, JVM_GetMethodParameters(JNIEnv *env, jobject method))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetMethodParameters, JVM_GetMethodParameters(JNIEnv *env, jobject method))
 {
   // method is a handle to a java.lang.reflect.Method object
   Method* method_ptr = jvm_get_method_common(method);
   methodHandle mh (THREAD, method_ptr);
   Handle reflected_method (THREAD, JNIHandles::resolve_non_null(method));
@@ -1646,11 +1659,11 @@
 }
 JVM_END
 
 // New (JDK 1.4) reflection implementation /////////////////////////////////////
 
-JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, jboolean publicOnly))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetClassDeclaredFields, JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, jboolean publicOnly))
 {
   JvmtiVMObjectAllocEventCollector oam;
 
   oop ofMirror = JNIHandles::resolve_non_null(ofClass);
   // Exclude primitive types and array types
@@ -1697,11 +1710,11 @@
 }
 JVM_END
 
 // A class is a record if and only if it is final and a direct subclass of
 // java.lang.Record and has a Record attribute; otherwise, it is not a record.
-JVM_ENTRY(jboolean, JVM_IsRecord(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jboolean, JVM_IsRecord, JVM_IsRecord(JNIEnv *env, jclass cls))
 {
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   if (k != nullptr && k->is_instance_klass()) {
     InstanceKlass* ik = InstanceKlass::cast(k);
     return ik->is_record();
@@ -1714,11 +1727,11 @@
 // Returns an array containing the components of the Record attribute,
 // or null if the attribute is not present.
 //
 // Note that this function returns the components of the Record attribute
 // even if the class is not a record.
-JVM_ENTRY(jobjectArray, JVM_GetRecordComponents(JNIEnv* env, jclass ofClass))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetRecordComponents, JVM_GetRecordComponents(JNIEnv* env, jclass ofClass))
 {
   Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass));
   assert(c->is_instance_klass(), "must be");
   InstanceKlass* ik = InstanceKlass::cast(c);
 
@@ -1821,27 +1834,27 @@
   }
 
   return (jobjectArray) JNIHandles::make_local(THREAD, result());
 }
 
-JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredMethods(JNIEnv *env, jclass ofClass, jboolean publicOnly))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetClassDeclaredMethods, JVM_GetClassDeclaredMethods(JNIEnv *env, jclass ofClass, jboolean publicOnly))
 {
   return get_class_declared_methods_helper(env, ofClass, publicOnly,
                                            /*want_constructor*/ false,
                                            vmClasses::reflect_Method_klass(), THREAD);
 }
 JVM_END
 
-JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetClassDeclaredConstructors, JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly))
 {
   return get_class_declared_methods_helper(env, ofClass, publicOnly,
                                            /*want_constructor*/ true,
                                            vmClasses::reflect_Constructor_klass(), THREAD);
 }
 JVM_END
 
-JVM_ENTRY(jint, JVM_GetClassAccessFlags(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jint, JVM_GetClassAccessFlags, JVM_GetClassAccessFlags(JNIEnv *env, jclass cls))
 {
   oop mirror = JNIHandles::resolve_non_null(cls);
   if (java_lang_Class::is_primitive(mirror)) {
     // Primitive type
     return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC;
@@ -1850,11 +1863,11 @@
   Klass* k = java_lang_Class::as_Klass(mirror);
   return k->access_flags().as_int() & JVM_ACC_WRITTEN_FLAGS;
 }
 JVM_END
 
-JVM_ENTRY(jboolean, JVM_AreNestMates(JNIEnv *env, jclass current, jclass member))
+JVM_ENTRY_PROF(jboolean, JVM_AreNestMates, JVM_AreNestMates(JNIEnv *env, jclass current, jclass member))
 {
   Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current));
   assert(c->is_instance_klass(), "must be");
   InstanceKlass* ck = InstanceKlass::cast(c);
   Klass* m = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(member));
@@ -1862,11 +1875,11 @@
   InstanceKlass* mk = InstanceKlass::cast(m);
   return ck->has_nestmate_access_to(mk, THREAD);
 }
 JVM_END
 
-JVM_ENTRY(jclass, JVM_GetNestHost(JNIEnv* env, jclass current))
+JVM_ENTRY_PROF(jclass, JVM_GetNestHost, JVM_GetNestHost(JNIEnv* env, jclass current))
 {
   // current is not a primitive or array class
   Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current));
   assert(c->is_instance_klass(), "must be");
   InstanceKlass* ck = InstanceKlass::cast(c);
@@ -1874,11 +1887,11 @@
   return (jclass) (host == nullptr ? nullptr :
                    JNIHandles::make_local(THREAD, host->java_mirror()));
 }
 JVM_END
 
-JVM_ENTRY(jobjectArray, JVM_GetNestMembers(JNIEnv* env, jclass current))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetNestMembers, JVM_GetNestMembers(JNIEnv* env, jclass current))
 {
   // current is not a primitive or array class
   ResourceMark rm(THREAD);
   Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current));
   assert(c->is_instance_klass(), "must be");
@@ -1954,11 +1967,11 @@
     return (jobjectArray)JNIHandles::make_local(THREAD, result());
   }
 }
 JVM_END
 
-JVM_ENTRY(jobjectArray, JVM_GetPermittedSubclasses(JNIEnv* env, jclass current))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetPermittedSubclasses, JVM_GetPermittedSubclasses(JNIEnv* env, jclass current))
 {
   oop mirror = JNIHandles::resolve_non_null(current);
   assert(!java_lang_Class::is_primitive(mirror), "should not be");
   Klass* c = java_lang_Class::as_Klass(mirror);
   assert(c->is_instance_klass(), "must be");
@@ -2017,11 +2030,11 @@
 }
 JVM_END
 
 // Constant pool access //////////////////////////////////////////////////////////
 
-JVM_ENTRY(jobject, JVM_GetClassConstantPool(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jobject, JVM_GetClassConstantPool, JVM_GetClassConstantPool(JNIEnv *env, jclass cls))
 {
   JvmtiVMObjectAllocEventCollector oam;
   oop mirror = JNIHandles::resolve_non_null(cls);
   // Return null for primitives and arrays
   if (!java_lang_Class::is_primitive(mirror)) {
@@ -2036,19 +2049,19 @@
   return nullptr;
 }
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_ConstantPoolGetSize(JNIEnv *env, jobject obj, jobject unused))
+JVM_ENTRY_PROF(jint, JVM_ConstantPoolGetSize, JVM_ConstantPoolGetSize(JNIEnv *env, jobject obj, jobject unused))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   return cp->length();
 }
 JVM_END
 
 
-JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jclass, JVM_ConstantPoolGetClassAt, JVM_ConstantPoolGetClassAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   constantTag tag = cp->tag_at(index);
   if (!tag.is_klass() && !tag.is_unresolved_klass()) {
@@ -2057,11 +2070,11 @@
   Klass* k = cp->klass_at(index, CHECK_NULL);
   return (jclass) JNIHandles::make_local(THREAD, k->java_mirror());
 }
 JVM_END
 
-JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jclass, JVM_ConstantPoolGetClassAtIfLoaded, JVM_ConstantPoolGetClassAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   constantTag tag = cp->tag_at(index);
   if (!tag.is_klass() && !tag.is_unresolved_klass()) {
@@ -2101,21 +2114,21 @@
     method = Reflection::new_method(m, true, CHECK_NULL);
   }
   return JNIHandles::make_local(THREAD, method);
 }
 
-JVM_ENTRY(jobject, JVM_ConstantPoolGetMethodAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jobject, JVM_ConstantPoolGetMethodAt, JVM_ConstantPoolGetMethodAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   jobject res = get_method_at_helper(cp, index, true, CHECK_NULL);
   return res;
 }
 JVM_END
 
-JVM_ENTRY(jobject, JVM_ConstantPoolGetMethodAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jobject, JVM_ConstantPoolGetMethodAtIfLoaded, JVM_ConstantPoolGetMethodAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   jobject res = get_method_at_helper(cp, index, false, CHECK_NULL);
@@ -2146,31 +2159,31 @@
   }
   oop field = Reflection::new_field(&fd, CHECK_NULL);
   return JNIHandles::make_local(THREAD, field);
 }
 
-JVM_ENTRY(jobject, JVM_ConstantPoolGetFieldAt(JNIEnv *env, jobject obj, jobject unusedl, jint index))
+JVM_ENTRY_PROF(jobject, JVM_ConstantPoolGetFieldAt, JVM_ConstantPoolGetFieldAt(JNIEnv *env, jobject obj, jobject unusedl, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   jobject res = get_field_at_helper(cp, index, true, CHECK_NULL);
   return res;
 }
 JVM_END
 
-JVM_ENTRY(jobject, JVM_ConstantPoolGetFieldAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jobject, JVM_ConstantPoolGetFieldAtIfLoaded, JVM_ConstantPoolGetFieldAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   jobject res = get_field_at_helper(cp, index, false, CHECK_NULL);
   return res;
 }
 JVM_END
 
-JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetMemberRefInfoAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jobjectArray, JVM_ConstantPoolGetMemberRefInfoAt, JVM_ConstantPoolGetMemberRefInfoAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   constantTag tag = cp->tag_at(index);
@@ -2191,11 +2204,11 @@
   dest->obj_at_put(2, str());
   return (jobjectArray) JNIHandles::make_local(THREAD, dest());
 }
 JVM_END
 
-JVM_ENTRY(jint, JVM_ConstantPoolGetClassRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jint, JVM_ConstantPoolGetClassRefIndexAt, JVM_ConstantPoolGetClassRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_0);
   constantTag tag = cp->tag_at(index);
@@ -2204,11 +2217,11 @@
   }
   return (jint) cp->uncached_klass_ref_index_at(index);
 }
 JVM_END
 
-JVM_ENTRY(jint, JVM_ConstantPoolGetNameAndTypeRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jint, JVM_ConstantPoolGetNameAndTypeRefIndexAt, JVM_ConstantPoolGetNameAndTypeRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_0);
   constantTag tag = cp->tag_at(index);
@@ -2217,11 +2230,11 @@
   }
   return (jint) cp->uncached_name_and_type_ref_index_at(index);
 }
 JVM_END
 
-JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetNameAndTypeRefInfoAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jobjectArray, JVM_ConstantPoolGetNameAndTypeRefInfoAt, JVM_ConstantPoolGetNameAndTypeRefInfoAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   constantTag tag = cp->tag_at(index);
@@ -2238,11 +2251,11 @@
   dest->obj_at_put(1, str());
   return (jobjectArray) JNIHandles::make_local(THREAD, dest());
 }
 JVM_END
 
-JVM_ENTRY(jint, JVM_ConstantPoolGetIntAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jint, JVM_ConstantPoolGetIntAt, JVM_ConstantPoolGetIntAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_0);
   constantTag tag = cp->tag_at(index);
   if (!tag.is_int()) {
@@ -2250,11 +2263,11 @@
   }
   return cp->int_at(index);
 }
 JVM_END
 
-JVM_ENTRY(jlong, JVM_ConstantPoolGetLongAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jlong, JVM_ConstantPoolGetLongAt, JVM_ConstantPoolGetLongAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_(0L));
   constantTag tag = cp->tag_at(index);
   if (!tag.is_long()) {
@@ -2262,11 +2275,11 @@
   }
   return cp->long_at(index);
 }
 JVM_END
 
-JVM_ENTRY(jfloat, JVM_ConstantPoolGetFloatAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jfloat, JVM_ConstantPoolGetFloatAt, JVM_ConstantPoolGetFloatAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_(0.0f));
   constantTag tag = cp->tag_at(index);
   if (!tag.is_float()) {
@@ -2274,11 +2287,11 @@
   }
   return cp->float_at(index);
 }
 JVM_END
 
-JVM_ENTRY(jdouble, JVM_ConstantPoolGetDoubleAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jdouble, JVM_ConstantPoolGetDoubleAt, JVM_ConstantPoolGetDoubleAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_(0.0));
   constantTag tag = cp->tag_at(index);
   if (!tag.is_double()) {
@@ -2286,11 +2299,11 @@
   }
   return cp->double_at(index);
 }
 JVM_END
 
-JVM_ENTRY(jstring, JVM_ConstantPoolGetStringAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jstring, JVM_ConstantPoolGetStringAt, JVM_ConstantPoolGetStringAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   constantTag tag = cp->tag_at(index);
   if (!tag.is_string()) {
@@ -2299,11 +2312,11 @@
   oop str = cp->string_at(index, CHECK_NULL);
   return (jstring) JNIHandles::make_local(THREAD, str);
 }
 JVM_END
 
-JVM_ENTRY(jstring, JVM_ConstantPoolGetUTF8At(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jstring, JVM_ConstantPoolGetUTF8At, JVM_ConstantPoolGetUTF8At(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   JvmtiVMObjectAllocEventCollector oam;
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_NULL);
   constantTag tag = cp->tag_at(index);
@@ -2314,11 +2327,11 @@
   Handle str = java_lang_String::create_from_symbol(sym, CHECK_NULL);
   return (jstring) JNIHandles::make_local(THREAD, str());
 }
 JVM_END
 
-JVM_ENTRY(jbyte, JVM_ConstantPoolGetTagAt(JNIEnv *env, jobject obj, jobject unused, jint index))
+JVM_ENTRY_PROF(jbyte, JVM_ConstantPoolGetTagAt, JVM_ConstantPoolGetTagAt(JNIEnv *env, jobject obj, jobject unused, jint index))
 {
   constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj)));
   bounds_check(cp, index, CHECK_0);
   constantTag tag = cp->tag_at(index);
   jbyte result = tag.value();
@@ -2340,11 +2353,11 @@
 }
 JVM_END
 
 // Assertion support. //////////////////////////////////////////////////////////
 
-JVM_ENTRY(jboolean, JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls))
+JVM_ENTRY_PROF(jboolean, JVM_DesiredAssertionStatus, JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls))
   assert(cls != nullptr, "bad class");
 
   oop r = JNIHandles::resolve(cls);
   assert(! java_lang_Class::is_primitive(r), "primitive classes not allowed");
   if (java_lang_Class::is_primitive(r)) return false;
@@ -2361,11 +2374,11 @@
 JVM_END
 
 
 // Return a new AssertionStatusDirectives object with the fields filled in with
 // command-line assertion arguments (i.e., -ea, -da).
-JVM_ENTRY(jobject, JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused))
+JVM_ENTRY_PROF(jobject, JVM_AssertionStatusDirectives, JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused))
   JvmtiVMObjectAllocEventCollector oam;
   oop asd = JavaAssertions::createAssertionStatusDirectives(CHECK_NULL);
   return JNIHandles::make_local(THREAD, asd);
 JVM_END
 
@@ -2379,18 +2392,18 @@
 // The function returns a Klass* of the _scratch_class if the verifier
 // was invoked in the middle of the class redefinition.
 // Otherwise it returns its argument value which is the _the_class Klass*.
 // Please, refer to the description in the jvmtiThreadSate.hpp.
 
-JVM_ENTRY(const char*, JVM_GetClassNameUTF(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(const char*, JVM_GetClassNameUTF, JVM_GetClassNameUTF(JNIEnv *env, jclass cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   return k->name()->as_utf8();
 JVM_END
 
 
-JVM_ENTRY(void, JVM_GetClassCPTypes(JNIEnv *env, jclass cls, unsigned char *types))
+JVM_ENTRY_PROF(void, JVM_GetClassCPTypes, JVM_GetClassCPTypes(JNIEnv *env, jclass cls, unsigned char *types))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   // types will have length zero if this is not an InstanceKlass
   // (length is determined by call to JVM_GetClassCPEntriesCount)
   if (k->is_instance_klass()) {
@@ -2401,25 +2414,25 @@
     }
   }
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetClassCPEntriesCount(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jint, JVM_GetClassCPEntriesCount, JVM_GetClassCPEntriesCount(JNIEnv *env, jclass cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   return (!k->is_instance_klass()) ? 0 : InstanceKlass::cast(k)->constants()->length();
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetClassFieldsCount(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jint, JVM_GetClassFieldsCount, JVM_GetClassFieldsCount(JNIEnv *env, jclass cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   return (!k->is_instance_klass()) ? 0 : InstanceKlass::cast(k)->java_fields_count();
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetClassMethodsCount(JNIEnv *env, jclass cls))
+JVM_ENTRY_PROF(jint, JVM_GetClassMethodsCount, JVM_GetClassMethodsCount(JNIEnv *env, jclass cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   return (!k->is_instance_klass()) ? 0 : InstanceKlass::cast(k)->methods()->length();
 JVM_END
 
@@ -2427,11 +2440,11 @@
 // The following methods, used for the verifier, are never called with
 // array klasses, so a direct cast to InstanceKlass is safe.
 // Typically, these methods are called in a loop with bounds determined
 // by the results of JVM_GetClass{Fields,Methods}Count, which return
 // zero for arrays.
-JVM_ENTRY(void, JVM_GetMethodIxExceptionIndexes(JNIEnv *env, jclass cls, jint method_index, unsigned short *exceptions))
+JVM_ENTRY_PROF(void, JVM_GetMethodIxExceptionIndexes, JVM_GetMethodIxExceptionIndexes(JNIEnv *env, jclass cls, jint method_index, unsigned short *exceptions))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   int length = method->checked_exceptions_length();
   if (length > 0) {
@@ -2441,35 +2454,35 @@
     }
   }
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetMethodIxExceptionsCount(JNIEnv *env, jclass cls, jint method_index))
+JVM_ENTRY_PROF(jint, JVM_GetMethodIxExceptionsCount, JVM_GetMethodIxExceptionsCount(JNIEnv *env, jclass cls, jint method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->checked_exceptions_length();
 JVM_END
 
 
-JVM_ENTRY(void, JVM_GetMethodIxByteCode(JNIEnv *env, jclass cls, jint method_index, unsigned char *code))
+JVM_ENTRY_PROF(void, JVM_GetMethodIxByteCode, JVM_GetMethodIxByteCode(JNIEnv *env, jclass cls, jint method_index, unsigned char *code))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   memcpy(code, method->code_base(), method->code_size());
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetMethodIxByteCodeLength(JNIEnv *env, jclass cls, jint method_index))
+JVM_ENTRY_PROF(jint, JVM_GetMethodIxByteCodeLength, JVM_GetMethodIxByteCodeLength(JNIEnv *env, jclass cls, jint method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->code_size();
 JVM_END
 
 
-JVM_ENTRY(void, JVM_GetMethodIxExceptionTableEntry(JNIEnv *env, jclass cls, jint method_index, jint entry_index, JVM_ExceptionTableEntryType *entry))
+JVM_ENTRY_PROF(void, JVM_GetMethodIxExceptionTableEntry, JVM_GetMethodIxExceptionTableEntry(JNIEnv *env, jclass cls, jint method_index, jint entry_index, JVM_ExceptionTableEntryType *entry))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   ExceptionTable extable(method);
   entry->start_pc   = extable.start_pc(entry_index);
@@ -2477,83 +2490,83 @@
   entry->handler_pc = extable.handler_pc(entry_index);
   entry->catchType  = extable.catch_type_index(entry_index);
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetMethodIxExceptionTableLength(JNIEnv *env, jclass cls, int method_index))
+JVM_ENTRY_PROF(jint, JVM_GetMethodIxExceptionTableLength, JVM_GetMethodIxExceptionTableLength(JNIEnv *env, jclass cls, int method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->exception_table_length();
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetMethodIxModifiers(JNIEnv *env, jclass cls, int method_index))
+JVM_ENTRY_PROF(jint, JVM_GetMethodIxModifiers, JVM_GetMethodIxModifiers(JNIEnv *env, jclass cls, int method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->access_flags().as_int() & JVM_RECOGNIZED_METHOD_MODIFIERS;
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetFieldIxModifiers(JNIEnv *env, jclass cls, int field_index))
+JVM_ENTRY_PROF(jint, JVM_GetFieldIxModifiers, JVM_GetFieldIxModifiers(JNIEnv *env, jclass cls, int field_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   return InstanceKlass::cast(k)->field_access_flags(field_index) & JVM_RECOGNIZED_FIELD_MODIFIERS;
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetMethodIxLocalsCount(JNIEnv *env, jclass cls, int method_index))
+JVM_ENTRY_PROF(jint, JVM_GetMethodIxLocalsCount, JVM_GetMethodIxLocalsCount(JNIEnv *env, jclass cls, int method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->max_locals();
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetMethodIxArgsSize(JNIEnv *env, jclass cls, int method_index))
+JVM_ENTRY_PROF(jint, JVM_GetMethodIxArgsSize, JVM_GetMethodIxArgsSize(JNIEnv *env, jclass cls, int method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->size_of_parameters();
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetMethodIxMaxStack(JNIEnv *env, jclass cls, int method_index))
+JVM_ENTRY_PROF(jint, JVM_GetMethodIxMaxStack, JVM_GetMethodIxMaxStack(JNIEnv *env, jclass cls, int method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->verifier_max_stack();
 JVM_END
 
 
-JVM_ENTRY(jboolean, JVM_IsConstructorIx(JNIEnv *env, jclass cls, int method_index))
+JVM_ENTRY_PROF(jboolean, JVM_IsConstructorIx, JVM_IsConstructorIx(JNIEnv *env, jclass cls, int method_index))
   ResourceMark rm(THREAD);
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->name() == vmSymbols::object_initializer_name();
 JVM_END
 
 
-JVM_ENTRY(jboolean, JVM_IsVMGeneratedMethodIx(JNIEnv *env, jclass cls, int method_index))
+JVM_ENTRY_PROF(jboolean, JVM_IsVMGeneratedMethodIx, JVM_IsVMGeneratedMethodIx(JNIEnv *env, jclass cls, int method_index))
   ResourceMark rm(THREAD);
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->is_overpass();
 JVM_END
 
-JVM_ENTRY(const char*, JVM_GetMethodIxNameUTF(JNIEnv *env, jclass cls, jint method_index))
+JVM_ENTRY_PROF(const char*, JVM_GetMethodIxNameUTF, JVM_GetMethodIxNameUTF(JNIEnv *env, jclass cls, jint method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->name()->as_utf8();
 JVM_END
 
 
-JVM_ENTRY(const char*, JVM_GetMethodIxSignatureUTF(JNIEnv *env, jclass cls, jint method_index))
+JVM_ENTRY_PROF(const char*, JVM_GetMethodIxSignatureUTF, JVM_GetMethodIxSignatureUTF(JNIEnv *env, jclass cls, jint method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   Method* method = InstanceKlass::cast(k)->methods()->at(method_index);
   return method->signature()->as_utf8();
 JVM_END
@@ -2564,11 +2577,11 @@
  * works on a copy of the code, it will not see any rewriting that
  * may possibly occur in the middle of verification.  So it is important
  * that nothing it calls tries to use the cpCache instead of the raw
  * constant pool, so we must use cp->uncached_x methods when appropriate.
  */
-JVM_ENTRY(const char*, JVM_GetCPFieldNameUTF(JNIEnv *env, jclass cls, jint cp_index))
+JVM_ENTRY_PROF(const char*, JVM_GetCPFieldNameUTF, JVM_GetCPFieldNameUTF(JNIEnv *env, jclass cls, jint cp_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
   switch (cp->tag_at(cp_index).value()) {
     case JVM_CONSTANT_Fieldref:
@@ -2579,11 +2592,11 @@
   ShouldNotReachHere();
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(const char*, JVM_GetCPMethodNameUTF(JNIEnv *env, jclass cls, jint cp_index))
+JVM_ENTRY_PROF(const char*, JVM_GetCPMethodNameUTF, JVM_GetCPMethodNameUTF(JNIEnv *env, jclass cls, jint cp_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
   switch (cp->tag_at(cp_index).value()) {
     case JVM_CONSTANT_InterfaceMethodref:
@@ -2595,11 +2608,11 @@
   ShouldNotReachHere();
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(const char*, JVM_GetCPMethodSignatureUTF(JNIEnv *env, jclass cls, jint cp_index))
+JVM_ENTRY_PROF(const char*, JVM_GetCPMethodSignatureUTF, JVM_GetCPMethodSignatureUTF(JNIEnv *env, jclass cls, jint cp_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
   switch (cp->tag_at(cp_index).value()) {
     case JVM_CONSTANT_InterfaceMethodref:
@@ -2611,11 +2624,11 @@
   ShouldNotReachHere();
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(const char*, JVM_GetCPFieldSignatureUTF(JNIEnv *env, jclass cls, jint cp_index))
+JVM_ENTRY_PROF(const char*, JVM_GetCPFieldSignatureUTF, JVM_GetCPFieldSignatureUTF(JNIEnv *env, jclass cls, jint cp_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
   switch (cp->tag_at(cp_index).value()) {
     case JVM_CONSTANT_Fieldref:
@@ -2626,20 +2639,20 @@
   ShouldNotReachHere();
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(const char*, JVM_GetCPClassNameUTF(JNIEnv *env, jclass cls, jint cp_index))
+JVM_ENTRY_PROF(const char*, JVM_GetCPClassNameUTF, JVM_GetCPClassNameUTF(JNIEnv *env, jclass cls, jint cp_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
   Symbol* classname = cp->klass_name_at(cp_index);
   return classname->as_utf8();
 JVM_END
 
 
-JVM_ENTRY(const char*, JVM_GetCPFieldClassNameUTF(JNIEnv *env, jclass cls, jint cp_index))
+JVM_ENTRY_PROF(const char*, JVM_GetCPFieldClassNameUTF, JVM_GetCPFieldClassNameUTF(JNIEnv *env, jclass cls, jint cp_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
   switch (cp->tag_at(cp_index).value()) {
     case JVM_CONSTANT_Fieldref: {
@@ -2653,11 +2666,11 @@
   ShouldNotReachHere();
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(const char*, JVM_GetCPMethodClassNameUTF(JNIEnv *env, jclass cls, jint cp_index))
+JVM_ENTRY_PROF(const char*, JVM_GetCPMethodClassNameUTF, JVM_GetCPMethodClassNameUTF(JNIEnv *env, jclass cls, jint cp_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   k = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
   switch (cp->tag_at(cp_index).value()) {
     case JVM_CONSTANT_Methodref:
@@ -2672,11 +2685,11 @@
   ShouldNotReachHere();
   return nullptr;
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetCPFieldModifiers(JNIEnv *env, jclass cls, int cp_index, jclass called_cls))
+JVM_ENTRY_PROF(jint, JVM_GetCPFieldModifiers, JVM_GetCPFieldModifiers(JNIEnv *env, jclass cls, int cp_index, jclass called_cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   Klass* k_called = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(called_cls));
   k        = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   k_called = JvmtiThreadState::class_to_verify_considering_redefinition(k_called, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
@@ -2699,11 +2712,11 @@
   ShouldNotReachHere();
   return 0;
 JVM_END
 
 
-JVM_ENTRY(jint, JVM_GetCPMethodModifiers(JNIEnv *env, jclass cls, int cp_index, jclass called_cls))
+JVM_ENTRY_PROF(jint, JVM_GetCPMethodModifiers, JVM_GetCPMethodModifiers(JNIEnv *env, jclass cls, int cp_index, jclass called_cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
   Klass* k_called = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(called_cls));
   k        = JvmtiThreadState::class_to_verify_considering_redefinition(k, thread);
   k_called = JvmtiThreadState::class_to_verify_considering_redefinition(k_called, thread);
   ConstantPool* cp = InstanceKlass::cast(k)->constants();
@@ -2730,16 +2743,16 @@
 JVM_END
 
 
 // Misc //////////////////////////////////////////////////////////////////////////////////////////////
 
-JVM_LEAF(void, JVM_ReleaseUTF(const char *utf))
+JVM_LEAF_PROF(void, JVM_ReleaseUTF, JVM_ReleaseUTF(const char *utf))
   // So long as UTF8::convert_to_utf8 returns resource strings, we don't have to do anything
 JVM_END
 
 
-JVM_ENTRY(jboolean, JVM_IsSameClassPackage(JNIEnv *env, jclass class1, jclass class2))
+JVM_ENTRY_PROF(jboolean, JVM_IsSameClassPackage, JVM_IsSameClassPackage(JNIEnv *env, jclass class1, jclass class2))
   oop class1_mirror = JNIHandles::resolve_non_null(class1);
   oop class2_mirror = JNIHandles::resolve_non_null(class2);
   Klass* klass1 = java_lang_Class::as_Klass(class1_mirror);
   Klass* klass2 = java_lang_Class::as_Klass(class2_mirror);
   return (jboolean) Reflection::is_same_class_package(klass1, klass2);
@@ -2834,11 +2847,11 @@
                           vmSymbols::void_method_signature(),
                           THREAD);
 }
 
 
-JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
+JVM_ENTRY_PROF(void, JVM_StartThread, JVM_StartThread(JNIEnv* env, jobject jthread))
 #if INCLUDE_CDS
   if (CDSConfig::allow_only_single_java_thread()) {
     // During java -Xshare:dump, if we allow multiple Java threads to
     // execute in parallel, symbols and classes may be loaded in
     // random orders which will make the resulting CDS archive
@@ -2931,11 +2944,11 @@
   Thread::start(native_thread);
 
 JVM_END
 
 
-JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio))
+JVM_ENTRY_PROF(void, JVM_SetThreadPriority, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio))
   ThreadsListHandle tlh(thread);
   oop java_thread = nullptr;
   JavaThread* receiver = nullptr;
   bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
   java_lang_Thread::set_priority(java_thread, (ThreadPriority)prio);
@@ -2948,16 +2961,16 @@
   // priority set in the java.lang.Thread object above will be pushed
   // down when it does start.
 JVM_END
 
 
-JVM_LEAF(void, JVM_Yield(JNIEnv *env, jclass threadClass))
+JVM_LEAF_PROF(void, JVM_Yield, JVM_Yield(JNIEnv *env, jclass threadClass))
   HOTSPOT_THREAD_YIELD();
   os::naked_yield();
 JVM_END
 
-JVM_ENTRY(void, JVM_SleepNanos(JNIEnv* env, jclass threadClass, jlong nanos))
+JVM_ENTRY_PROF(void, JVM_SleepNanos, JVM_SleepNanos(JNIEnv* env, jclass threadClass, jlong nanos))
   if (nanos < 0) {
     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nanosecond timeout value out of range");
   }
 
   if (thread->is_interrupted(true) && !HAS_PENDING_EXCEPTION) {
@@ -2989,38 +3002,38 @@
     thread->osthread()->set_state(old_state);
   }
   HOTSPOT_THREAD_SLEEP_END(0);
 JVM_END
 
-JVM_ENTRY(jobject, JVM_CurrentCarrierThread(JNIEnv* env, jclass threadClass))
+JVM_ENTRY_PROF(jobject, JVM_CurrentCarrierThread, JVM_CurrentCarrierThread(JNIEnv* env, jclass threadClass))
   oop jthread = thread->threadObj();
   assert(jthread != nullptr, "no current carrier thread!");
   return JNIHandles::make_local(THREAD, jthread);
 JVM_END
 
-JVM_ENTRY(jobject, JVM_CurrentThread(JNIEnv* env, jclass threadClass))
+JVM_ENTRY_PROF(jobject, JVM_CurrentThread, JVM_CurrentThread(JNIEnv* env, jclass threadClass))
   oop theThread = thread->vthread();
   assert(theThread != (oop)nullptr, "no current thread!");
   return JNIHandles::make_local(THREAD, theThread);
 JVM_END
 
-JVM_ENTRY(void, JVM_SetCurrentThread(JNIEnv* env, jobject thisThread,
+JVM_ENTRY_PROF(void, JVM_SetCurrentThread, JVM_SetCurrentThread(JNIEnv* env, jobject thisThread,
                                      jobject theThread))
   oop threadObj = JNIHandles::resolve(theThread);
   thread->set_vthread(threadObj);
 
   // Set lock id of new current Thread
   thread->set_lock_id(java_lang_Thread::thread_id(threadObj));
 
   JFR_ONLY(Jfr::on_set_current_thread(thread, threadObj);)
 JVM_END
 
-JVM_ENTRY(jlong, JVM_GetNextThreadIdOffset(JNIEnv* env, jclass threadClass))
+JVM_ENTRY_PROF(jlong, JVM_GetNextThreadIdOffset, JVM_GetNextThreadIdOffset(JNIEnv* env, jclass threadClass))
   return ThreadIdentifier::unsafe_offset();
 JVM_END
 
-JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
+JVM_ENTRY_PROF(void, JVM_Interrupt, JVM_Interrupt(JNIEnv* env, jobject jthread))
   ThreadsListHandle tlh(thread);
   JavaThread* receiver = nullptr;
   bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, nullptr);
   if (is_alive) {
     // jthread refers to a live JavaThread.
@@ -3028,24 +3041,25 @@
   }
 JVM_END
 
 // Return true iff the current thread has locked the object passed in
 
-JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj))
+JVM_ENTRY_PROF(jboolean, JVM_HoldsLock, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj))
   if (obj == nullptr) {
     THROW_(vmSymbols::java_lang_NullPointerException(), JNI_FALSE);
   }
   Handle h_obj(THREAD, JNIHandles::resolve(obj));
   return ObjectSynchronizer::current_thread_holds_lock(thread, h_obj);
 JVM_END
 
-JVM_ENTRY(jobject, JVM_GetStackTrace(JNIEnv *env, jobject jthread))
+JVM_ENTRY_PROF(jobject, JVM_GetStackTrace, JVM_GetStackTrace(JNIEnv *env, jobject jthread))
   oop trace = java_lang_Thread::async_get_stack_trace(JNIHandles::resolve(jthread), THREAD);
   return JNIHandles::make_local(THREAD, trace);
 JVM_END
 
-JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
+
+JVM_ENTRY_PROF(void, JVM_SetNativeThreadName, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
   // We don't use a ThreadsListHandle here because the current thread
   // must be alive.
   oop java_thread = JNIHandles::resolve_non_null(jthread);
   JavaThread* thr = java_lang_Thread::thread(java_thread);
   if (thread == thr && !thr->has_attached_via_jni()) {
@@ -3056,24 +3070,24 @@
     const char *thread_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
     os::set_native_thread_name(thread_name);
   }
 JVM_END
 
-JVM_ENTRY(jobject, JVM_ScopedValueCache(JNIEnv* env, jclass threadClass))
+JVM_ENTRY_PROF(jobject, JVM_ScopedValueCache, JVM_ScopedValueCache(JNIEnv* env, jclass threadClass))
   oop theCache = thread->scopedValueCache();
   return JNIHandles::make_local(THREAD, theCache);
 JVM_END
 
-JVM_ENTRY(void, JVM_SetScopedValueCache(JNIEnv* env, jclass threadClass,
+JVM_ENTRY_PROF(void, JVM_SetScopedValueCache, JVM_SetScopedValueCache(JNIEnv* env, jclass threadClass,
                                        jobject theCache))
   arrayOop objs = arrayOop(JNIHandles::resolve(theCache));
   thread->set_scopedValueCache(objs);
 JVM_END
 
 // java.lang.SecurityManager ///////////////////////////////////////////////////////////////////////
 
-JVM_ENTRY(jobjectArray, JVM_GetClassContext(JNIEnv *env))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetClassContext, JVM_GetClassContext(JNIEnv *env))
   ResourceMark rm(THREAD);
   JvmtiVMObjectAllocEventCollector oam;
   vframeStream vfst(thread);
 
   if (vmClasses::reflect_CallerSensitive_klass() != nullptr) {
@@ -3110,60 +3124,60 @@
 
 
 // java.lang.Package ////////////////////////////////////////////////////////////////
 
 
-JVM_ENTRY(jstring, JVM_GetSystemPackage(JNIEnv *env, jstring name))
+JVM_ENTRY_PROF(jstring, JVM_GetSystemPackage, JVM_GetSystemPackage(JNIEnv *env, jstring name))
   ResourceMark rm(THREAD);
   JvmtiVMObjectAllocEventCollector oam;
   char* str = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
   oop result = ClassLoader::get_system_package(str, CHECK_NULL);
 return (jstring) JNIHandles::make_local(THREAD, result);
 JVM_END
 
 
-JVM_ENTRY(jobjectArray, JVM_GetSystemPackages(JNIEnv *env))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetSystemPackages, JVM_GetSystemPackages(JNIEnv *env))
   JvmtiVMObjectAllocEventCollector oam;
   objArrayOop result = ClassLoader::get_system_packages(CHECK_NULL);
   return (jobjectArray) JNIHandles::make_local(THREAD, result);
 JVM_END
 
 
 // java.lang.ref.Reference ///////////////////////////////////////////////////////////////
 
 
-JVM_ENTRY(jobject, JVM_GetAndClearReferencePendingList(JNIEnv* env))
+JVM_ENTRY_PROF(jobject, JVM_GetAndClearReferencePendingList, JVM_GetAndClearReferencePendingList(JNIEnv* env))
   MonitorLocker ml(Heap_lock);
   oop ref = Universe::reference_pending_list();
   if (ref != nullptr) {
     Universe::clear_reference_pending_list();
   }
   return JNIHandles::make_local(THREAD, ref);
 JVM_END
 
-JVM_ENTRY(jboolean, JVM_HasReferencePendingList(JNIEnv* env))
+JVM_ENTRY_PROF(jboolean, JVM_HasReferencePendingList, JVM_HasReferencePendingList(JNIEnv* env))
   MonitorLocker ml(Heap_lock);
   return Universe::has_reference_pending_list();
 JVM_END
 
-JVM_ENTRY(void, JVM_WaitForReferencePendingList(JNIEnv* env))
+JVM_ENTRY_PROF(void, JVM_WaitForReferencePendingList, JVM_WaitForReferencePendingList(JNIEnv* env))
   MonitorLocker ml(Heap_lock);
   while (!Universe::has_reference_pending_list()) {
     ml.wait();
   }
 JVM_END
 
-JVM_ENTRY(jboolean, JVM_ReferenceRefersTo(JNIEnv* env, jobject ref, jobject o))
+JVM_ENTRY_PROF(jboolean, JVM_ReferenceRefersTo, JVM_ReferenceRefersTo(JNIEnv* env, jobject ref, jobject o))
   oop ref_oop = JNIHandles::resolve_non_null(ref);
   // PhantomReference has it's own implementation of refersTo().
   // See: JVM_PhantomReferenceRefersTo
   assert(!java_lang_ref_Reference::is_phantom(ref_oop), "precondition");
   oop referent = java_lang_ref_Reference::weak_referent_no_keepalive(ref_oop);
   return referent == JNIHandles::resolve(o);
 JVM_END
 
-JVM_ENTRY(void, JVM_ReferenceClear(JNIEnv* env, jobject ref))
+JVM_ENTRY_PROF(void, JVM_ReferenceClear, JVM_ReferenceClear(JNIEnv* env, jobject ref))
   oop ref_oop = JNIHandles::resolve_non_null(ref);
   // FinalReference has it's own implementation of clear().
   assert(!java_lang_ref_Reference::is_final(ref_oop), "precondition");
   if (java_lang_ref_Reference::unknown_referent_no_keepalive(ref_oop) == nullptr) {
     // If the referent has already been cleared then done.
@@ -3181,11 +3195,11 @@
 
 
 // java.lang.ref.PhantomReference //////////////////////////////////////////////////
 
 
-JVM_ENTRY(jboolean, JVM_PhantomReferenceRefersTo(JNIEnv* env, jobject ref, jobject o))
+JVM_ENTRY_PROF(jboolean, JVM_PhantomReferenceRefersTo, JVM_PhantomReferenceRefersTo(JNIEnv* env, jobject ref, jobject o))
   oop ref_oop = JNIHandles::resolve_non_null(ref);
   oop referent = java_lang_ref_Reference::phantom_referent_no_keepalive(ref_oop);
   return referent == JNIHandles::resolve(o);
 JVM_END
 
@@ -3193,11 +3207,11 @@
 // ObjectInputStream ///////////////////////////////////////////////////////////////
 
 // Return the first user-defined class loader up the execution stack, or null
 // if only code from the bootstrap or platform class loader is on the stack.
 
-JVM_ENTRY(jobject, JVM_LatestUserDefinedLoader(JNIEnv *env))
+JVM_ENTRY_PROF(jobject, JVM_LatestUserDefinedLoader, JVM_LatestUserDefinedLoader(JNIEnv *env))
   for (vframeStream vfst(thread); !vfst.at_end(); vfst.next()) {
     InstanceKlass* ik = vfst.method()->method_holder();
     oop loader = ik->class_loader();
     if (loader != nullptr && !SystemDictionary::is_platform_class_loader(loader)) {
       return JNIHandles::make_local(THREAD, loader);
@@ -3223,27 +3237,27 @@
   }
   return arrayOop(a);
 }
 
 
-JVM_ENTRY(jint, JVM_GetArrayLength(JNIEnv *env, jobject arr))
+JVM_ENTRY_PROF(jint, JVM_GetArrayLength, JVM_GetArrayLength(JNIEnv *env, jobject arr))
   arrayOop a = check_array(env, arr, false, CHECK_0);
   return a->length();
 JVM_END
 
 
-JVM_ENTRY(jobject, JVM_GetArrayElement(JNIEnv *env, jobject arr, jint index))
+JVM_ENTRY_PROF(jobject, JVM_GetArrayElement, JVM_GetArrayElement(JNIEnv *env, jobject arr, jint index))
   JvmtiVMObjectAllocEventCollector oam;
   arrayOop a = check_array(env, arr, false, CHECK_NULL);
   jvalue value;
   BasicType type = Reflection::array_get(&value, a, index, CHECK_NULL);
   oop box = Reflection::box(&value, type, CHECK_NULL);
   return JNIHandles::make_local(THREAD, box);
 JVM_END
 
 
-JVM_ENTRY(jvalue, JVM_GetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jint wCode))
+JVM_ENTRY_PROF(jvalue, JVM_GetPrimitiveArrayElement, JVM_GetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jint wCode))
   jvalue value;
   value.i = 0; // to initialize value before getting used in CHECK
   arrayOop a = check_array(env, arr, true, CHECK_(value));
   assert(a->is_typeArray(), "just checking");
   BasicType type = Reflection::array_get(&value, a, index, CHECK_(value));
@@ -3253,11 +3267,11 @@
   }
   return value;
 JVM_END
 
 
-JVM_ENTRY(void, JVM_SetArrayElement(JNIEnv *env, jobject arr, jint index, jobject val))
+JVM_ENTRY_PROF(void, JVM_SetArrayElement, JVM_SetArrayElement(JNIEnv *env, jobject arr, jint index, jobject val))
   arrayOop a = check_array(env, arr, false, CHECK);
   oop box = JNIHandles::resolve(val);
   jvalue value;
   value.i = 0; // to initialize value before getting used in CHECK
   BasicType value_type;
@@ -3269,27 +3283,27 @@
   }
   Reflection::array_set(&value, a, index, value_type, CHECK);
 JVM_END
 
 
-JVM_ENTRY(void, JVM_SetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jvalue v, unsigned char vCode))
+JVM_ENTRY_PROF(void, JVM_SetPrimitiveArrayElement, JVM_SetPrimitiveArrayElement(JNIEnv *env, jobject arr, jint index, jvalue v, unsigned char vCode))
   arrayOop a = check_array(env, arr, true, CHECK);
   assert(a->is_typeArray(), "just checking");
   BasicType value_type = (BasicType) vCode;
   Reflection::array_set(&v, a, index, value_type, CHECK);
 JVM_END
 
 
-JVM_ENTRY(jobject, JVM_NewArray(JNIEnv *env, jclass eltClass, jint length))
+JVM_ENTRY_PROF(jobject, JVM_NewArray, JVM_NewArray(JNIEnv *env, jclass eltClass, jint length))
   JvmtiVMObjectAllocEventCollector oam;
   oop element_mirror = JNIHandles::resolve(eltClass);
   oop result = Reflection::reflect_new_array(element_mirror, length, CHECK_NULL);
   return JNIHandles::make_local(THREAD, result);
 JVM_END
 
 
-JVM_ENTRY(jobject, JVM_NewMultiArray(JNIEnv *env, jclass eltClass, jintArray dim))
+JVM_ENTRY_PROF(jobject, JVM_NewMultiArray, JVM_NewMultiArray(JNIEnv *env, jclass eltClass, jintArray dim))
   JvmtiVMObjectAllocEventCollector oam;
   arrayOop dim_array = check_array(env, dim, true, CHECK_NULL);
   oop element_mirror = JNIHandles::resolve(eltClass);
   assert(dim_array->is_typeArray(), "just checking");
   oop result = Reflection::reflect_new_multi_array(element_mirror, typeArrayOop(dim_array), CHECK_NULL);
@@ -3297,15 +3311,15 @@
 JVM_END
 
 
 // Library support ///////////////////////////////////////////////////////////////////////////
 
-JVM_LEAF(void*, JVM_LoadZipLibrary())
+JVM_LEAF_PROF(void*, JVM_LoadZipLibrary, JVM_LoadZipLibrary())
   return ZipLibrary::handle();
 JVM_END
 
-JVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* name, jboolean throwException))
+JVM_ENTRY_NO_ENV_PROF(void*, JVM_LoadLibrary, JVM_LoadLibrary(const char* name, jboolean throwException))
   //%note jvm_ct
   char ebuf[1024];
   void *load_result;
   {
     ThreadToNativeFromVM ttnfvm(thread);
@@ -3333,51 +3347,51 @@
   log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, name, p2i(load_result));
   return load_result;
 JVM_END
 
 
-JVM_LEAF(void, JVM_UnloadLibrary(void* handle))
+JVM_LEAF_PROF(void, JVM_UnloadLibrary, JVM_UnloadLibrary(void* handle))
   os::dll_unload(handle);
   log_info(library)("Unloaded library with handle " INTPTR_FORMAT, p2i(handle));
 JVM_END
 
 
-JVM_LEAF(void*, JVM_FindLibraryEntry(void* handle, const char* name))
+JVM_LEAF_PROF(void*, JVM_FindLibraryEntry, JVM_FindLibraryEntry(void* handle, const char* name))
   void* find_result = os::dll_lookup(handle, name);
   log_info(library)("%s %s in library with handle " INTPTR_FORMAT,
                     find_result != nullptr ? "Found" : "Failed to find",
                     name, p2i(handle));
   return find_result;
 JVM_END
 
 
 // JNI version ///////////////////////////////////////////////////////////////////////////////
 
-JVM_LEAF(jboolean, JVM_IsSupportedJNIVersion(jint version))
+JVM_LEAF_PROF(jboolean, JVM_IsSupportedJNIVersion, JVM_IsSupportedJNIVersion(jint version))
   return Threads::is_supported_jni_version_including_1_1(version);
 JVM_END
 
 
-JVM_LEAF(jboolean, JVM_IsPreviewEnabled(void))
+JVM_LEAF_PROF(jboolean, JVM_IsPreviewEnabled, JVM_IsPreviewEnabled(void))
   return Arguments::enable_preview() ? JNI_TRUE : JNI_FALSE;
 JVM_END
 
-JVM_LEAF(jboolean, JVM_IsContinuationsSupported(void))
+JVM_LEAF_PROF(jboolean, JVM_IsContinuationsSupported, JVM_IsContinuationsSupported(void))
   return VMContinuations ? JNI_TRUE : JNI_FALSE;
 JVM_END
 
-JVM_LEAF(jboolean, JVM_IsForeignLinkerSupported(void))
+JVM_LEAF_PROF(jboolean, JVM_IsForeignLinkerSupported, JVM_IsForeignLinkerSupported(void))
   return ForeignGlobals::is_foreign_linker_supported() ? JNI_TRUE : JNI_FALSE;
 JVM_END
 
 JVM_ENTRY_NO_ENV(jboolean, JVM_IsStaticallyLinked(void))
   return is_vm_statically_linked() ? JNI_TRUE : JNI_FALSE;
 JVM_END
 
 // String support ///////////////////////////////////////////////////////////////////////////
 
-JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
+JVM_ENTRY_PROF(jstring, JVM_InternString, JVM_InternString(JNIEnv *env, jstring str))
   JvmtiVMObjectAllocEventCollector oam;
   if (str == nullptr) return nullptr;
   oop string = JNIHandles::resolve_non_null(str);
   oop result = StringTable::intern(string, CHECK_NULL);
   return (jstring) JNIHandles::make_local(THREAD, result);
@@ -3439,11 +3453,11 @@
 }
 
 
 // Method ///////////////////////////////////////////////////////////////////////////////////////////
 
-JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))
+JVM_ENTRY_PROF(jobject, JVM_InvokeMethod, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))
   Handle method_handle;
   if (thread->stack_overflow_state()->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {
     method_handle = Handle(THREAD, JNIHandles::resolve(method));
     Handle receiver(THREAD, JNIHandles::resolve(obj));
     objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));
@@ -3463,28 +3477,28 @@
     THROW_NULL(vmSymbols::java_lang_StackOverflowError());
   }
 JVM_END
 
 
-JVM_ENTRY(jobject, JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjectArray args0))
+JVM_ENTRY_PROF(jobject, JVM_NewInstanceFromConstructor, JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjectArray args0))
   oop constructor_mirror = JNIHandles::resolve(c);
   objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));
   oop result = Reflection::invoke_constructor(constructor_mirror, args, CHECK_NULL);
   jobject res = JNIHandles::make_local(THREAD, result);
   if (JvmtiExport::should_post_vm_object_alloc()) {
     JvmtiExport::post_vm_object_alloc(thread, result);
   }
   return res;
 JVM_END
 
-JVM_ENTRY(void, JVM_InitializeFromArchive(JNIEnv* env, jclass cls))
+JVM_ENTRY_PROF(void, JVM_InitializeFromArchive, JVM_InitializeFromArchive(JNIEnv* env, jclass cls))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls));
   assert(k->is_klass(), "just checking");
   HeapShared::initialize_from_archived_subgraph(THREAD, k);
 JVM_END
 
-JVM_ENTRY(void, JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env,
+JVM_ENTRY_PROF(void, JVM_RegisterLambdaProxyClassForArchiving, JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env,
                                               jclass caller,
                                               jstring interfaceMethodName,
                                               jobject factoryType,
                                               jobject interfaceMethodType,
                                               jobject implementationMember,
@@ -3528,11 +3542,11 @@
   SystemDictionaryShared::add_lambda_proxy_class(caller_ik, lambda_ik, interface_method_name, factory_type,
                                                  interface_method_type, m, dynamic_method_type, THREAD);
 #endif // INCLUDE_CDS
 JVM_END
 
-JVM_ENTRY(jclass, JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env,
+JVM_ENTRY_PROF(jclass, JVM_LookupLambdaProxyClassFromArchive, JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env,
                                                         jclass caller,
                                                         jstring interfaceMethodName,
                                                         jobject factoryType,
                                                         jobject interfaceMethodType,
                                                         jobject implementationMember,
@@ -3576,11 +3590,11 @@
 #else
   return nullptr;
 #endif // INCLUDE_CDS
 JVM_END
 
-JVM_ENTRY_NO_ENV(jlong, JVM_GetRandomSeedForDumping())
+JVM_ENTRY_NO_ENV_PROF(jlong, JVM_GetRandomSeedForDumping, JVM_GetRandomSeedForDumping())
   if (CDSConfig::is_dumping_static_archive()) {
     // We do this so that the default CDS archive can be deterministic.
     const char* release = VM_Version::vm_release();
     const char* dbg_level = VM_Version::jdk_debug_level();
     const char* version = VM_Version::internal_vm_info_string();
@@ -3599,22 +3613,22 @@
   } else {
     return 0;
   }
 JVM_END
 
-JVM_ENTRY_NO_ENV(jint, JVM_GetCDSConfigStatus())
+JVM_ENTRY_NO_ENV_PROF(jint, JVM_GetCDSConfigStatus, JVM_GetCDSConfigStatus())
   return CDSConfig::get_status();
 JVM_END
 
-JVM_ENTRY(void, JVM_LogLambdaFormInvoker(JNIEnv *env, jstring line))
+JVM_ENTRY_PROF(void, JVM_LogLambdaFormInvoker, JVM_LogLambdaFormInvoker(JNIEnv *env, jstring line))
 #if INCLUDE_CDS
   assert(CDSConfig::is_logging_lambda_form_invokers(), "sanity");
   if (line != nullptr) {
     ResourceMark rm(THREAD);
     Handle h_line (THREAD, JNIHandles::resolve_non_null(line));
     char* c_line = java_lang_String::as_utf8_string(h_line());
-    if (CDSConfig::is_dumping_dynamic_archive()) {
+    if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) {
       // Note: LambdaFormInvokers::append take same format which is not
       // same as below the print format. The line does not include LAMBDA_FORM_TAG.
       LambdaFormInvokers::append(os::strdup((const char*)c_line, mtInternal));
     }
     if (ClassListWriter::is_enabled()) {
@@ -3623,20 +3637,34 @@
     }
   }
 #endif // INCLUDE_CDS
 JVM_END
 
-JVM_ENTRY(void, JVM_DumpClassListToFile(JNIEnv *env, jstring listFileName))
+JVM_ENTRY_PROF(void, JVM_LogDynamicProxy, JVM_LogDynamicProxy(JNIEnv *env, jobject loader, jstring proxy_name, jobjectArray interfaces, jint access_flags))
+#if INCLUDE_CDS
+  assert(CDSConfig::is_logging_dynamic_proxies(),  "sanity");
+
+  ResourceMark rm(THREAD);
+  oop proxy_name_oop = JNIHandles::resolve_non_null(proxy_name);
+  char* proxy_name_str = java_lang_String::as_utf8_string(proxy_name_oop);
+  oop loader_oop = JNIHandles::resolve(loader);
+  objArrayOop interfaces_oop = objArrayOop(JNIHandles::resolve_non_null(interfaces));
+
+  AOTConstantPoolResolver::trace_dynamic_proxy_class(loader_oop, proxy_name_str, interfaces_oop, access_flags);
+#endif // INCLUDE_CDS
+JVM_END
+
+JVM_ENTRY_PROF(void, JVM_DumpClassListToFile, JVM_DumpClassListToFile(JNIEnv *env, jstring listFileName))
 #if INCLUDE_CDS
   ResourceMark rm(THREAD);
   Handle file_handle(THREAD, JNIHandles::resolve_non_null(listFileName));
   char* file_name  = java_lang_String::as_utf8_string(file_handle());
   MetaspaceShared::dump_loaded_classes(file_name, THREAD);
 #endif // INCLUDE_CDS
 JVM_END
 
-JVM_ENTRY(void, JVM_DumpDynamicArchive(JNIEnv *env, jstring archiveName))
+JVM_ENTRY_PROF(void, JVM_DumpDynamicArchive, JVM_DumpDynamicArchive(JNIEnv *env, jstring archiveName))
 #if INCLUDE_CDS
   ResourceMark rm(THREAD);
   Handle file_handle(THREAD, JNIHandles::resolve_non_null(archiveName));
   char* archive_name  = java_lang_String::as_utf8_string(file_handle());
   DynamicArchive::dump_for_jcmd(archive_name, CHECK);
@@ -3644,11 +3672,11 @@
 JVM_END
 
 // Returns an array of all live Thread objects (VM internal JavaThreads,
 // jvmti agent threads, and JNI attaching threads  are skipped)
 // See CR 6404306 regarding JNI attaching threads
-JVM_ENTRY(jobjectArray, JVM_GetAllThreads(JNIEnv *env, jclass dummy))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetAllThreads, JVM_GetAllThreads(JNIEnv *env, jclass dummy))
   ResourceMark rm(THREAD);
   ThreadsListEnumerator tle(THREAD, false, false);
   JvmtiVMObjectAllocEventCollector oam;
 
   int num_threads = tle.num_threads();
@@ -3665,11 +3693,11 @@
 
 
 // Support for java.lang.Thread.getStackTrace() and getAllStackTraces() methods
 // Return StackTraceElement[][], each element is the stack trace of a thread in
 // the corresponding entry in the given threads array
-JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobjectArray threads))
+JVM_ENTRY_PROF(jobjectArray, JVM_DumpThreads, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobjectArray threads))
   JvmtiVMObjectAllocEventCollector oam;
 
   // Check if threads is null
   if (threads == nullptr) {
     THROW_NULL(vmSymbols::java_lang_NullPointerException());
@@ -3704,29 +3732,29 @@
   return (jobjectArray)JNIHandles::make_local(THREAD, stacktraces());
 
 JVM_END
 
 // JVM monitoring and management support
-JVM_LEAF(void*, JVM_GetManagement(jint version))
+JVM_LEAF_PROF(void*, JVM_GetManagement, JVM_GetManagement(jint version))
   return Management::get_jmm_interface(version);
 JVM_END
 
 // com.sun.tools.attach.VirtualMachine agent properties support
 //
 // Initialize the agent properties with the properties maintained in the VM
-JVM_ENTRY(jobject, JVM_InitAgentProperties(JNIEnv *env, jobject properties))
+JVM_ENTRY_PROF(jobject, JVM_InitAgentProperties, JVM_InitAgentProperties(JNIEnv *env, jobject properties))
   ResourceMark rm;
 
   Handle props(THREAD, JNIHandles::resolve_non_null(properties));
 
   PUTPROP(props, "sun.java.command", Arguments::java_command());
   PUTPROP(props, "sun.jvm.flags", Arguments::jvm_flags());
   PUTPROP(props, "sun.jvm.args", Arguments::jvm_args());
   return properties;
 JVM_END
 
-JVM_ENTRY(jobjectArray, JVM_GetEnclosingMethodInfo(JNIEnv *env, jclass ofClass))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetEnclosingMethodInfo, JVM_GetEnclosingMethodInfo(JNIEnv *env, jclass ofClass))
 {
   JvmtiVMObjectAllocEventCollector oam;
 
   if (ofClass == nullptr) {
     return nullptr;
@@ -3765,11 +3793,11 @@
   return (jobjectArray) JNIHandles::make_local(THREAD, dest());
 }
 JVM_END
 
 // Returns an array of java.lang.String objects containing the input arguments to the VM.
-JVM_ENTRY(jobjectArray, JVM_GetVmArguments(JNIEnv *env))
+JVM_ENTRY_PROF(jobjectArray, JVM_GetVmArguments, JVM_GetVmArguments(JNIEnv *env))
   ResourceMark rm(THREAD);
 
   if (Arguments::num_jvm_args() == 0 && Arguments::num_jvm_flags() == 0) {
     return nullptr;
   }
@@ -3793,15 +3821,15 @@
     result_h->obj_at_put(index, h());
   }
   return (jobjectArray) JNIHandles::make_local(THREAD, result_h());
 JVM_END
 
-JVM_LEAF(jint, JVM_FindSignal(const char *name))
+JVM_LEAF_PROF(jint, JVM_FindSignal, JVM_FindSignal(const char *name))
   return os::get_signal_number(name);
 JVM_END
 
-JVM_ENTRY(void, JVM_VirtualThreadStart(JNIEnv* env, jobject vthread))
+JVM_ENTRY_PROF(void, JVM_VirtualThreadStart, JVM_VirtualThreadStart(JNIEnv* env, jobject vthread))
 #if INCLUDE_JVMTI
   if (!DoJVMTIVirtualThreadTransitions) {
     assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
     return;
   }
@@ -3812,11 +3840,11 @@
     JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(thread, vthread, false);
   }
 #endif
 JVM_END
 
-JVM_ENTRY(void, JVM_VirtualThreadEnd(JNIEnv* env, jobject vthread))
+JVM_ENTRY_PROF(void, JVM_VirtualThreadEnd, JVM_VirtualThreadEnd(JNIEnv* env, jobject vthread))
 #if INCLUDE_JVMTI
   if (!DoJVMTIVirtualThreadTransitions) {
     assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
     return;
   }
@@ -3829,11 +3857,11 @@
 #endif
 JVM_END
 
 // If notifications are disabled then just update the VTMS transition bit and return.
 // Otherwise, the bit is updated in the given jvmtiVTMSTransitionDisabler function call.
-JVM_ENTRY(void, JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide))
+JVM_ENTRY_PROF(void, JVM_VirtualThreadMount, JVM_VirtualThreadMount(JNIEnv* env, jobject vthread, jboolean hide))
 #if INCLUDE_JVMTI
   if (!DoJVMTIVirtualThreadTransitions) {
     assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
     return;
   }
@@ -3846,11 +3874,11 @@
 #endif
 JVM_END
 
 // If notifications are disabled then just update the VTMS transition bit and return.
 // Otherwise, the bit is updated in the given jvmtiVTMSTransitionDisabler function call below.
-JVM_ENTRY(void, JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide))
+JVM_ENTRY_PROF(void, JVM_VirtualThreadUnmount, JVM_VirtualThreadUnmount(JNIEnv* env, jobject vthread, jboolean hide))
 #if INCLUDE_JVMTI
   if (!DoJVMTIVirtualThreadTransitions) {
     assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
     return;
   }
@@ -3913,11 +3941,11 @@
 /*
  * Return the current class's class file version.  The low order 16 bits of the
  * returned jint contain the class's major version.  The high order 16 bits
  * contain the class's minor version.
  */
-JVM_ENTRY(jint, JVM_GetClassFileVersion(JNIEnv* env, jclass current))
+JVM_ENTRY_PROF(jint, JVM_GetClassFileVersion, JVM_GetClassFileVersion(JNIEnv* env, jclass current))
   oop mirror = JNIHandles::resolve_non_null(current);
   if (java_lang_Class::is_primitive(mirror)) {
     // return latest major version and minor version of 0.
     return JVM_CLASSFILE_MAJOR_VERSION;
   }
@@ -3932,15 +3960,246 @@
 /*
  * Ensure that code doing a stackwalk and using javaVFrame::locals() to
  * get the value will see a materialized value and not a scalar-replaced
  * null value.
  */
-JVM_ENTRY(void, JVM_EnsureMaterializedForStackWalk_func(JNIEnv* env, jobject vthread, jobject value))
+JVM_ENTRY_PROF(void, JVM_EnsureMaterializedForStackWalk_func, JVM_EnsureMaterializedForStackWalk_func(JNIEnv* env, jobject vthread, jobject value))
   JVM_EnsureMaterializedForStackWalk(env, value);
 JVM_END
 
 /*
  * Return JNI_TRUE if warnings are printed when agents are dynamically loaded.
  */
-JVM_LEAF(jboolean, JVM_PrintWarningAtDynamicAgentLoad(void))
+JVM_LEAF_PROF(jboolean, JVM_PrintWarningAtDynamicAgentLoad, JVM_PrintWarningAtDynamicAgentLoad(void))
   return (EnableDynamicAgentLoading && !FLAG_IS_CMDLINE(EnableDynamicAgentLoading)) ? JNI_TRUE : JNI_FALSE;
 JVM_END
+
+#define DO_COUNTERS(macro) \
+  macro(JVM_CurrentTimeMillis) \
+  macro(JVM_NanoTime) \
+  macro(JVM_GetNanoTimeAdjustment) \
+  macro(JVM_ArrayCopy) \
+  macro(JVM_GetProperties) \
+  macro(JVM_GetTemporaryDirectory) \
+  macro(JVM_BeforeHalt) \
+  macro(JVM_Halt) \
+  macro(JVM_GC) \
+  macro(JVM_MaxObjectInspectionAge) \
+  macro(JVM_TotalMemory) \
+  macro(JVM_FreeMemory) \
+  macro(JVM_MaxMemory) \
+  macro(JVM_ActiveProcessorCount) \
+  macro(JVM_IsUseContainerSupport) \
+  macro(JVM_FillInStackTrace) \
+  macro(JVM_GetExtendedNPEMessage) \
+  macro(JVM_InitStackTraceElementArray) \
+  macro(JVM_InitStackTraceElement) \
+  macro(JVM_ExpandStackFrameInfo) \
+  macro(JVM_CallStackWalk) \
+  macro(JVM_MoreStackWalk) \
+  macro(JVM_SetStackWalkContinuation) \
+  macro(JVM_IHashCode) \
+  macro(JVM_MonitorWait) \
+  macro(JVM_MonitorNotify) \
+  macro(JVM_MonitorNotifyAll) \
+  macro(JVM_Clone) \
+  macro(JVM_ReportFinalizationComplete) \
+  macro(JVM_IsFinalizationEnabled) \
+  macro(JVM_RegisterContinuationMethods) \
+  macro(JVM_NativePath) \
+  macro(JVM_GetCallerClass) \
+  macro(JVM_FindPrimitiveClass) \
+  macro(JVM_FindClassFromBootLoader) \
+  macro(JVM_FindClassFromCaller) \
+  macro(JVM_FindClassFromClass) \
+  macro(JVM_DefineClass) \
+  macro(JVM_LookupDefineClass) \
+  macro(JVM_DefineClassWithSource) \
+  macro(JVM_FindLoadedClass) \
+  macro(JVM_DefineModule) \
+  macro(JVM_SetBootLoaderUnnamedModule) \
+  macro(JVM_AddModuleExports) \
+  macro(JVM_AddModuleExportsToAllUnnamed) \
+  macro(JVM_AddModuleExportsToAll) \
+  macro(JVM_AddReadsModule) \
+  macro(JVM_DefineArchivedModules) \
+  macro(JVM_InitClassName) \
+  macro(JVM_GetClassInterfaces) \
+  macro(JVM_IsInterface) \
+  macro(JVM_IsHiddenClass) \
+  macro(JVM_GetProtectionDomain) \
+  macro(JVM_FindScopedValueBindings) \
+  macro(JVM_IsArrayClass) \
+  macro(JVM_IsPrimitiveClass) \
+  macro(JVM_GetClassModifiers) \
+  macro(JVM_GetDeclaredClasses) \
+  macro(JVM_GetDeclaringClass) \
+  macro(JVM_GetSimpleBinaryName) \
+  macro(JVM_GetClassSignature) \
+  macro(JVM_GetClassAnnotations) \
+  macro(JVM_GetClassTypeAnnotations) \
+  macro(JVM_GetMethodTypeAnnotations) \
+  macro(JVM_GetFieldTypeAnnotations) \
+  macro(JVM_GetMethodParameters) \
+  macro(JVM_GetClassDeclaredFields) \
+  macro(JVM_IsRecord) \
+  macro(JVM_GetRecordComponents) \
+  macro(JVM_GetClassDeclaredMethods) \
+  macro(JVM_GetClassDeclaredConstructors) \
+  macro(JVM_GetClassAccessFlags) \
+  macro(JVM_AreNestMates) \
+  macro(JVM_GetNestHost) \
+  macro(JVM_GetNestMembers) \
+  macro(JVM_GetPermittedSubclasses) \
+  macro(JVM_GetClassConstantPool) \
+  macro(JVM_ConstantPoolGetSize) \
+  macro(JVM_ConstantPoolGetClassAt) \
+  macro(JVM_ConstantPoolGetClassAtIfLoaded) \
+  macro(JVM_ConstantPoolGetMethodAt) \
+  macro(JVM_ConstantPoolGetMethodAtIfLoaded) \
+  macro(JVM_ConstantPoolGetFieldAt) \
+  macro(JVM_ConstantPoolGetFieldAtIfLoaded) \
+  macro(JVM_ConstantPoolGetMemberRefInfoAt) \
+  macro(JVM_ConstantPoolGetClassRefIndexAt) \
+  macro(JVM_ConstantPoolGetNameAndTypeRefIndexAt) \
+  macro(JVM_ConstantPoolGetNameAndTypeRefInfoAt) \
+  macro(JVM_ConstantPoolGetIntAt) \
+  macro(JVM_ConstantPoolGetLongAt) \
+  macro(JVM_ConstantPoolGetFloatAt) \
+  macro(JVM_ConstantPoolGetDoubleAt) \
+  macro(JVM_ConstantPoolGetStringAt) \
+  macro(JVM_ConstantPoolGetUTF8At) \
+  macro(JVM_ConstantPoolGetTagAt) \
+  macro(JVM_DesiredAssertionStatus) \
+  macro(JVM_AssertionStatusDirectives) \
+  macro(JVM_GetClassNameUTF) \
+  macro(JVM_GetClassCPTypes) \
+  macro(JVM_GetClassCPEntriesCount) \
+  macro(JVM_GetClassFieldsCount) \
+  macro(JVM_GetClassMethodsCount) \
+  macro(JVM_GetMethodIxExceptionIndexes) \
+  macro(JVM_GetMethodIxExceptionsCount) \
+  macro(JVM_GetMethodIxByteCode) \
+  macro(JVM_GetMethodIxByteCodeLength) \
+  macro(JVM_GetMethodIxExceptionTableEntry) \
+  macro(JVM_GetMethodIxExceptionTableLength) \
+  macro(JVM_GetMethodIxModifiers) \
+  macro(JVM_GetFieldIxModifiers) \
+  macro(JVM_GetMethodIxLocalsCount) \
+  macro(JVM_GetMethodIxArgsSize) \
+  macro(JVM_GetMethodIxMaxStack) \
+  macro(JVM_IsConstructorIx) \
+  macro(JVM_IsVMGeneratedMethodIx) \
+  macro(JVM_GetMethodIxNameUTF) \
+  macro(JVM_GetMethodIxSignatureUTF) \
+  macro(JVM_GetCPFieldNameUTF) \
+  macro(JVM_GetCPMethodNameUTF) \
+  macro(JVM_GetCPMethodSignatureUTF) \
+  macro(JVM_GetCPFieldSignatureUTF) \
+  macro(JVM_GetCPClassNameUTF) \
+  macro(JVM_GetCPFieldClassNameUTF) \
+  macro(JVM_GetCPMethodClassNameUTF) \
+  macro(JVM_GetCPFieldModifiers) \
+  macro(JVM_GetCPMethodModifiers) \
+  macro(JVM_ReleaseUTF) \
+  macro(JVM_IsSameClassPackage) \
+  macro(JVM_StartThread) \
+  macro(JVM_SetThreadPriority) \
+  macro(JVM_Yield) \
+  macro(JVM_SleepNanos) \
+  macro(JVM_CurrentCarrierThread) \
+  macro(JVM_CurrentThread) \
+  macro(JVM_SetCurrentThread) \
+  macro(JVM_GetNextThreadIdOffset) \
+  macro(JVM_Interrupt) \
+  macro(JVM_HoldsLock) \
+  macro(JVM_GetStackTrace) \
+  macro(JVM_SetNativeThreadName) \
+  macro(JVM_ScopedValueCache) \
+  macro(JVM_SetScopedValueCache) \
+  macro(JVM_GetClassContext) \
+  macro(JVM_GetSystemPackage) \
+  macro(JVM_GetSystemPackages) \
+  macro(JVM_GetAndClearReferencePendingList) \
+  macro(JVM_HasReferencePendingList) \
+  macro(JVM_WaitForReferencePendingList) \
+  macro(JVM_ReferenceRefersTo) \
+  macro(JVM_ReferenceClear) \
+  macro(JVM_PhantomReferenceRefersTo) \
+  macro(JVM_LatestUserDefinedLoader) \
+  macro(JVM_GetArrayLength) \
+  macro(JVM_GetArrayElement) \
+  macro(JVM_GetPrimitiveArrayElement) \
+  macro(JVM_SetArrayElement) \
+  macro(JVM_SetPrimitiveArrayElement) \
+  macro(JVM_NewArray) \
+  macro(JVM_NewMultiArray) \
+  macro(JVM_LoadZipLibrary) \
+  macro(JVM_LoadLibrary) \
+  macro(JVM_UnloadLibrary) \
+  macro(JVM_FindLibraryEntry) \
+  macro(JVM_IsSupportedJNIVersion) \
+  macro(JVM_IsPreviewEnabled) \
+  macro(JVM_IsContinuationsSupported) \
+  macro(JVM_IsForeignLinkerSupported) \
+  macro(JVM_InternString) \
+  macro(JVM_InvokeMethod) \
+  macro(JVM_NewInstanceFromConstructor) \
+  macro(JVM_InitializeFromArchive) \
+  macro(JVM_RegisterLambdaProxyClassForArchiving) \
+  macro(JVM_LookupLambdaProxyClassFromArchive) \
+  macro(JVM_GetRandomSeedForDumping) \
+  macro(JVM_LogLambdaFormInvoker) \
+  macro(JVM_LogDynamicProxy) \
+  macro(JVM_DumpClassListToFile) \
+  macro(JVM_DumpDynamicArchive) \
+  macro(JVM_GetAllThreads) \
+  macro(JVM_DumpThreads) \
+  macro(JVM_GetManagement) \
+  macro(JVM_InitAgentProperties) \
+  macro(JVM_GetEnclosingMethodInfo) \
+  macro(JVM_GetVmArguments) \
+  macro(JVM_FindSignal) \
+  macro(JVM_VirtualThreadStart) \
+  macro(JVM_VirtualThreadEnd) \
+  macro(JVM_VirtualThreadMount) \
+  macro(JVM_VirtualThreadUnmount) \
+  macro(JVM_GetClassFileVersion) \
+  macro(JVM_EnsureMaterializedForStackWalk_func) \
+  macro(JVM_PrintWarningAtDynamicAgentLoad)
+
+#define INIT_COUNTER(name) \
+    NEWPERFTICKCOUNTERS(_perf_##name##_timer, SUN_RT, #name); \
+    NEWPERFEVENTCOUNTER(_perf_##name##_count, SUN_RT, #name "_count"); \
+
+void perf_jvm_init() {
+  if (ProfileVMCalls && UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS(INIT_COUNTER)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("jvm_perf_init failed unexpectedly");
+    }
+  }
+}
+
+#undef INIT_COUNTER
+
+#define PRINT_COUNTER(name) {\
+  jlong count = _perf_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-40s = " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) (" JLONG_FORMAT_W(5) " events)", \
+                 #name,                                                   \
+                 _perf_##name##_timer->elapsed_counter_value_us(),        \
+                 _perf_##name##_timer->thread_counter_value_us(), count); \
+  }}
+
+void perf_jvm_print_on(outputStream* st) {
+  if (ProfileVMCalls && UsePerfData) {
+    DO_COUNTERS(PRINT_COUNTER)
+  } else {
+    st->print_cr("  JVM:  no info (%s is disabled)", (UsePerfData ? "ProfileVMCalls" : "UsePerfData"));
+  }
+}
+
+#undef PRINT_COUNTER
diff a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -666,11 +666,11 @@
     // The phase is checked by the wrapper that called this function,
     // but this thread could be racing with the thread that is
     // terminating the VM so we check one more time.
 
     // create the zip entry
-    ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment, true);
+    ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment);
     if (zip_entry == nullptr) {
       return JVMTI_ERROR_ILLEGAL_ARGUMENT;
     }
 
     // add the jar file to the bootclasspath
@@ -708,11 +708,11 @@
     JavaThread* THREAD = JavaThread::current(); // For exception macros.
     HandleMark hm(THREAD);
 
     // create the zip entry (which will open the zip file and hence
     // check that the segment is indeed a zip file).
-    ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment, false);
+    ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment);
     if (zip_entry == nullptr) {
       return JVMTI_ERROR_ILLEGAL_ARGUMENT;
     }
     delete zip_entry;   // no longer needed
 
diff a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp
--- a/src/hotspot/share/prims/methodHandles.cpp
+++ b/src/hotspot/share/prims/methodHandles.cpp
@@ -21,14 +21,16 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/cds_globals.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "code/codeCache.hpp"
 #include "code/dependencyContext.hpp"
 #include "compiler/compileBroker.hpp"
 #include "interpreter/interpreter.hpp"
@@ -49,18 +51,21 @@
 #include "prims/methodHandles.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/jniHandles.inline.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/timerTrace.hpp"
 #include "runtime/reflection.hpp"
 #include "runtime/safepointVerifiers.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "sanitizers/leak.hpp"
+#include "services/management.hpp"
 #include "utilities/exceptions.hpp"
 
 
 /*
  * JSR 292 reference implementation: method handles
@@ -1042,11 +1047,11 @@
 #undef VALUE_COMMA
 #undef STRING_NULL
 #undef EACH_NAMED_CON
 #endif // PRODUCT
 
-JVM_ENTRY(jint, MHN_getNamedCon(JNIEnv *env, jobject igcls, jint which, jobjectArray box_jh)) {
+JVM_ENTRY_PROF(jint, MHN_getNamedCon, MHN_getNamedCon(JNIEnv *env, jobject igcls, jint which, jobjectArray box_jh)) {
 #ifndef PRODUCT
   if (advertise_con_value(which)) {
     assert(which >= 0 && which < con_value_count, "");
     int con = con_values[which];
     objArrayHandle box(THREAD, (objArrayOop) JNIHandles::resolve(box_jh));
@@ -1063,29 +1068,29 @@
   return 0;
 }
 JVM_END
 
 // void init(MemberName self, AccessibleObject ref)
-JVM_ENTRY(void, MHN_init_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jobject target_jh)) {
+JVM_ENTRY_PROF(void, MHN_init_Mem, MHN_init_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jobject target_jh)) {
   if (mname_jh == nullptr) { THROW_MSG(vmSymbols::java_lang_InternalError(), "mname is null"); }
   if (target_jh == nullptr) { THROW_MSG(vmSymbols::java_lang_InternalError(), "target is null"); }
   Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
   Handle target(THREAD, JNIHandles::resolve_non_null(target_jh));
   MethodHandles::init_MemberName(mname, target, CHECK);
 }
 JVM_END
 
 // void expand(MemberName self)
-JVM_ENTRY(void, MHN_expand_Mem(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+JVM_ENTRY_PROF(void, MHN_expand_Mem, MHN_expand_Mem(JNIEnv *env, jobject igcls, jobject mname_jh)) {
   if (mname_jh == nullptr) { THROW_MSG(vmSymbols::java_lang_InternalError(), "mname is null"); }
   Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
   MethodHandles::expand_MemberName(mname, 0, CHECK);
 }
 JVM_END
 
 // void resolve(MemberName self, Class<?> caller)
-JVM_ENTRY(jobject, MHN_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jclass caller_jh,
+JVM_ENTRY_PROF(jobject, MHN_resolve_Mem, MHN_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jclass caller_jh,
     jint lookup_mode, jboolean speculative_resolve)) {
   if (mname_jh == nullptr) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "mname is null"); }
   Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
 
   // The trusted Java code that calls this method should already have performed
@@ -1167,29 +1172,29 @@
   const char* msg = (must_be_static ? "static field required" : "non-static field required");
   THROW_MSG_0(vmSymbols::java_lang_InternalError(), msg);
   return 0;
 }
 
-JVM_ENTRY(jlong, MHN_objectFieldOffset(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+JVM_ENTRY_PROF(jlong, MHN_objectFieldOffset, MHN_objectFieldOffset(JNIEnv *env, jobject igcls, jobject mname_jh)) {
   return find_member_field_offset(JNIHandles::resolve(mname_jh), false, THREAD);
 }
 JVM_END
 
-JVM_ENTRY(jlong, MHN_staticFieldOffset(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+JVM_ENTRY_PROF(jlong, MHN_staticFieldOffset, MHN_staticFieldOffset(JNIEnv *env, jobject igcls, jobject mname_jh)) {
   return find_member_field_offset(JNIHandles::resolve(mname_jh), true, THREAD);
 }
 JVM_END
 
-JVM_ENTRY(jobject, MHN_staticFieldBase(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+JVM_ENTRY_PROF(jobject, MHN_staticFieldBase, MHN_staticFieldBase(JNIEnv *env, jobject igcls, jobject mname_jh)) {
   // use the other function to perform sanity checks:
   jlong ignore = find_member_field_offset(JNIHandles::resolve(mname_jh), true, CHECK_NULL);
   oop clazz = java_lang_invoke_MemberName::clazz(JNIHandles::resolve_non_null(mname_jh));
   return JNIHandles::make_local(THREAD, clazz);
 }
 JVM_END
 
-JVM_ENTRY(jobject, MHN_getMemberVMInfo(JNIEnv *env, jobject igcls, jobject mname_jh)) {
+JVM_ENTRY_PROF(jobject, MHN_getMemberVMInfo, MHN_getMemberVMInfo(JNIEnv *env, jobject igcls, jobject mname_jh)) {
   if (mname_jh == nullptr)  return nullptr;
   Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
   intptr_t vmindex  = java_lang_invoke_MemberName::vmindex(mname());
   objArrayHandle result = oopFactory::new_objArray_handle(vmClasses::Object_klass(), 2, CHECK_NULL);
   jvalue vmindex_value; vmindex_value.j = (long)vmindex;
@@ -1207,11 +1212,11 @@
   result->obj_at_put(1, x);
   return JNIHandles::make_local(THREAD, result());
 }
 JVM_END
 
-JVM_ENTRY(void, MHN_setCallSiteTargetNormal(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
+JVM_ENTRY_PROF(void, MHN_setCallSiteTargetNormal, MHN_setCallSiteTargetNormal(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
   Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
   Handle target   (THREAD, JNIHandles::resolve_non_null(target_jh));
   DeoptimizationScope deopt_scope;
   {
     // Walk all nmethods depending on this call site.
@@ -1223,11 +1228,43 @@
     deopt_scope.deoptimize_marked();
   }
 }
 JVM_END
 
-JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
+// Make sure the class is linked. If the class cannot be verified, etc, an exception will be thrown.
+JVM_ENTRY_PROF(void, MHN_checkArchivable, MHN_checkArchivable(JNIEnv *env, jobject igcls, jclass klass_jh)) {
+  if (klass_jh == nullptr) { THROW_MSG(vmSymbols::java_lang_InternalError(), "klass is null"); }
+
+  if (CDSConfig::is_dumping_invokedynamic()) {
+    Klass* klass = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(klass_jh));
+    if (klass != nullptr && klass->is_instance_klass()) {
+      // klass could be null during very early VM start-up
+      InstanceKlass* ik = InstanceKlass::cast(klass);
+      ik->link_class(THREAD); // exception will be thrown if unverifiable
+      if (!ik->is_linked()) {
+        assert(HAS_PENDING_EXCEPTION, "must be");
+        ResourceMark rm;
+        log_warning(cds)("Cannot use unverifiable class %s in MethodType",
+                         klass->external_name());
+        return;
+      }
+
+      if (SystemDictionaryShared::is_jfr_event_class(ik)) {
+        ResourceMark rm;
+        log_warning(cds)("Cannot use JFR event class %s in MethodType",
+                         klass->external_name());
+        Exceptions::fthrow(THREAD_AND_LOCATION,
+                           vmSymbols::java_lang_NoClassDefFoundError(),
+                           "Class %s, or one of its supertypes, is a JFR event class",
+                           klass->external_name());
+      }
+    }
+  }
+}
+JVM_END
+
+JVM_ENTRY_PROF(void, MHN_setCallSiteTargetVolatile, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
   Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
   Handle target   (THREAD, JNIHandles::resolve_non_null(target_jh));
   DeoptimizationScope deopt_scope;
   {
     // Walk all nmethods depending on this call site.
@@ -1239,11 +1276,11 @@
     deopt_scope.deoptimize_marked();
   }
 }
 JVM_END
 
-JVM_ENTRY(void, MHN_copyOutBootstrapArguments(JNIEnv* env, jobject igcls,
+JVM_ENTRY_PROF(void, MHN_copyOutBootstrapArguments, MHN_copyOutBootstrapArguments(JNIEnv* env, jobject igcls,
                                               jobject caller_jh, jintArray index_info_jh,
                                               jint start, jint end,
                                               jobjectArray buf_jh, jint pos,
                                               jboolean resolve, jobject ifna_jh)) {
   Klass* caller_k = java_lang_Class::as_Klass(JNIHandles::resolve(caller_jh));
@@ -1322,11 +1359,11 @@
 }
 JVM_END
 
 // It is called by a Cleaner object which ensures that dropped CallSites properly
 // deallocate their dependency information.
-JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) {
+JVM_ENTRY_PROF(void, MHN_clearCallSiteContext, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) {
   Handle context(THREAD, JNIHandles::resolve_non_null(context_jh));
   DeoptimizationScope deopt_scope;
   {
     NoSafepointVerifier nsv;
     MutexLocker ml(THREAD, CodeCache_lock, Mutex::_no_safepoint_check_flag);
@@ -1342,22 +1379,22 @@
 /**
  * Throws a java/lang/UnsupportedOperationException unconditionally.
  * This is required by the specification of MethodHandle.invoke if
  * invoked directly.
  */
-JVM_ENTRY(jobject, MH_invoke_UOE(JNIEnv* env, jobject mh, jobjectArray args)) {
+JVM_ENTRY_PROF(jobject, MH_invoke_UOE, MH_invoke_UOE(JNIEnv* env, jobject mh, jobjectArray args)) {
   THROW_MSG_NULL(vmSymbols::java_lang_UnsupportedOperationException(), "MethodHandle.invoke cannot be invoked reflectively");
   return nullptr;
 }
 JVM_END
 
 /**
  * Throws a java/lang/UnsupportedOperationException unconditionally.
  * This is required by the specification of MethodHandle.invokeExact if
  * invoked directly.
  */
-JVM_ENTRY(jobject, MH_invokeExact_UOE(JNIEnv* env, jobject mh, jobjectArray args)) {
+JVM_ENTRY_PROF(jobject, MH_invokeExact_UOE, MH_invokeExact_UOE(JNIEnv* env, jobject mh, jobjectArray args)) {
   THROW_MSG_NULL(vmSymbols::java_lang_UnsupportedOperationException(), "MethodHandle.invokeExact cannot be invoked reflectively");
   return nullptr;
 }
 JVM_END
 
@@ -1448,11 +1485,11 @@
 };
 
 /**
  * This one function is exported, used by NativeLookup.
  */
-JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) {
+JVM_ENTRY_PROF(void, JVM_RegisterMethodHandleMethods, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) {
   assert(!MethodHandles::enabled(), "must not be enabled");
   assert(vmClasses::MethodHandle_klass() != nullptr, "should be present");
   assert(vmClasses::VarHandle_klass() != nullptr, "should be present");
 
   oop mh_mirror = vmClasses::MethodHandle_klass()->java_mirror();
@@ -1479,5 +1516,60 @@
   log_debug(methodhandles, indy)("MethodHandle support loaded (using LambdaForms)");
 
   MethodHandles::set_enabled(true);
 }
 JVM_END
+
+#define DO_COUNTERS(macro)             \
+  macro(MHN_init_Mem)                  \
+  macro(MHN_expand_Mem)                \
+  macro(MHN_resolve_Mem)               \
+  macro(MHN_checkArchivable)           \
+  macro(MHN_getNamedCon)               \
+  macro(MHN_objectFieldOffset)         \
+  macro(MHN_setCallSiteTargetNormal)   \
+  macro(MHN_setCallSiteTargetVolatile) \
+  macro(MHN_copyOutBootstrapArguments) \
+  macro(MHN_clearCallSiteContext)      \
+  macro(MHN_staticFieldOffset)         \
+  macro(MHN_staticFieldBase)           \
+  macro(MHN_getMemberVMInfo)           \
+  macro(MH_invoke_UOE)                 \
+  macro(MH_invokeExact_UOE)            \
+  macro(JVM_RegisterMethodHandleMethods)
+
+#define INIT_COUNTER(name) \
+    NEWPERFTICKCOUNTERS(_perf_##name##_timer, SUN_RT, #name); \
+    NEWPERFEVENTCOUNTER(_perf_##name##_count, SUN_RT, #name "_count"); \
+
+void MethodHandles::init_counters() {
+  if (UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS(INIT_COUNTER)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("perf_mhn_init failed unexpectedly");
+    }
+  }
+}
+
+#undef INIT_COUNTER
+
+#define PRINT_COUNTER(name) {\
+  jlong count = _perf_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-40s = " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) (" JLONG_FORMAT_W(5) " events)", #name, \
+                 _perf_##name##_timer->elapsed_counter_value_us(), \
+                 _perf_##name##_timer->thread_counter_value_us(), \
+                 count); \
+  }}
+
+void MethodHandles::print_counters_on(outputStream* st) {
+  if (ProfileVMCalls && UsePerfData) {
+    DO_COUNTERS(PRINT_COUNTER)
+  } else {
+    st->print_cr("  MHN:  no info (%s is disabled)", (UsePerfData ? "ProfileVMCalls" : "UsePerfData"));
+  }
+}
+
+#undef PRINT_COUNTER
diff a/src/hotspot/share/prims/methodHandles.hpp b/src/hotspot/share/prims/methodHandles.hpp
--- a/src/hotspot/share/prims/methodHandles.hpp
+++ b/src/hotspot/share/prims/methodHandles.hpp
@@ -185,10 +185,14 @@
 #include CPU_HEADER(methodHandles)
 
   // Tracing
   static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
   static void trace_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid);
+
+public:
+  static void init_counters();
+  static void print_counters_on(outputStream* st);
 };
 
 //------------------------------------------------------------------------------
 // MethodHandlesAdapterGenerator
 //
diff a/src/hotspot/share/prims/perf.cpp b/src/hotspot/share/prims/perf.cpp
--- a/src/hotspot/share/prims/perf.cpp
+++ b/src/hotspot/share/prims/perf.cpp
@@ -35,16 +35,10 @@
 
 /*
  *      Implementation of class jdk.internal.perf.Perf
  */
 
-
-#define PERF_ENTRY(result_type, header) \
-  JVM_ENTRY(result_type, header)
-
-#define PERF_END JVM_END
-
 #define PerfWrapper(arg) /* Unimplemented at this time */
 
 static char* jstr_to_utf(JNIEnv *env, jstring str, TRAPS) {
 
   char* utfstr = nullptr;
@@ -62,11 +56,11 @@
   env->GetStringUTFRegion(str, 0, unicode_len, utfstr);
 
   return utfstr;
 }
 
-PERF_ENTRY(jobject, Perf_Attach(JNIEnv *env, jobject unused, int vmid))
+JVM_ENTRY(jobject, Perf_Attach(JNIEnv *env, jobject unused, int vmid))
 
   PerfWrapper("Perf_Attach");
 
   char* address = nullptr;
   size_t capacity = 0;
@@ -77,13 +71,13 @@
   {
     ThreadToNativeFromVM ttnfv(thread);
     return env->NewDirectByteBuffer(address, (jlong)capacity);
   }
 
-PERF_END
+JVM_END
 
-PERF_ENTRY(void, Perf_Detach(JNIEnv *env, jobject unused, jobject buffer))
+JVM_ENTRY(void, Perf_Detach(JNIEnv *env, jobject unused, jobject buffer))
 
   PerfWrapper("Perf_Detach");
 
   if (!UsePerfData) {
     // With -XX:-UsePerfData, detach is just a NOP
@@ -100,13 +94,13 @@
    capacity = env->GetDirectBufferCapacity(buffer);
   }
 
   PerfMemory::detach((char*)address, capacity);
 
-PERF_END
+JVM_END
 
-PERF_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name,
+JVM_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name,
            int variability, int units, jlong value))
 
   PerfWrapper("Perf_CreateLong");
 
   char* name_utf = nullptr;
@@ -161,13 +155,13 @@
   {
     ThreadToNativeFromVM ttnfv(thread);
     return env->NewDirectByteBuffer(lp, sizeof(jlong));
   }
 
-PERF_END
+JVM_END
 
-PERF_ENTRY(jobject, Perf_CreateByteArray(JNIEnv *env, jobject perf,
+JVM_ENTRY(jobject, Perf_CreateByteArray(JNIEnv *env, jobject perf,
                                          jstring name, jint variability,
                                          jint units, jbyteArray value,
                                          jint maxlength))
 
   PerfWrapper("Perf_CreateByteArray");
@@ -244,37 +238,37 @@
   {
     ThreadToNativeFromVM ttnfv(thread);
     return env->NewDirectByteBuffer(cp, maxlength+1);
   }
 
-PERF_END
+JVM_END
 
-PERF_ENTRY(jlong, Perf_HighResCounter(JNIEnv *env, jobject perf))
+JVM_ENTRY(jlong, Perf_HighResCounter(JNIEnv *env, jobject perf))
 
   PerfWrapper("Perf_HighResCounter");
 
   // this should be a method in java.lang.System. This value could
   // be acquired through access to a PerfData performance counter, but
   // doing so would require that the PerfData monitoring overhead be
   // incurred by all Java applications, which is unacceptable.
 
   return os::elapsed_counter();
 
-PERF_END
+JVM_END
 
-PERF_ENTRY(jlong, Perf_HighResFrequency(JNIEnv *env, jobject perf))
+JVM_ENTRY(jlong, Perf_HighResFrequency(JNIEnv *env, jobject perf))
 
   PerfWrapper("Perf_HighResFrequency");
 
   // this should be a method in java.lang.System. This value could
   // be acquired through access to a PerfData performance counter, but
   // doing so would require that the PerfData monitoring overhead be
   // incurred by all Java applications, which is unacceptable.
 
   return os::elapsed_frequency();
 
-PERF_END
+JVM_END
 
 /// JVM_RegisterPerfMethods
 
 #define CC (char*)  /*cast a literal from (const char*)*/
 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
diff a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp
--- a/src/hotspot/share/prims/unsafe.cpp
+++ b/src/hotspot/share/prims/unsafe.cpp
@@ -352,11 +352,11 @@
 
 ////// Allocation requests
 
 UNSAFE_ENTRY(jobject, Unsafe_AllocateInstance(JNIEnv *env, jobject unsafe, jclass cls)) {
   JvmtiVMObjectAllocEventCollector oam;
-  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(cls), CHECK_NULL);
+  instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(cls), "unsafe", CHECK_NULL);
   return JNIHandles::make_local(THREAD, i);
 } UNSAFE_END
 
 UNSAFE_LEAF(jlong, Unsafe_AllocateMemory0(JNIEnv *env, jobject unsafe, jlong size)) {
   size_t sz = (size_t)size;
diff a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -1098,11 +1098,11 @@
   DirectiveSet* directive = DirectivesStack::getMatchingDirective(mh, comp);
   bool is_blocking = !directive->BackgroundCompilationOption;
   DirectivesStack::release(directive);
 
   // Compile method and check result
-  nmethod* nm = CompileBroker::compile_method(mh, bci, comp_level, mh, mh->invocation_count(), CompileTask::Reason_Whitebox, CHECK_false);
+  nmethod* nm = CompileBroker::compile_method(mh, bci, comp_level, mh, mh->invocation_count(), false, CompileTask::Reason_Whitebox, CHECK_false);
   MutexLocker mu(THREAD, Compile_lock);
   bool is_queued = mh->queued_for_compilation();
   if ((!is_blocking && is_queued) || nm != nullptr) {
     return true;
   }
diff a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #include "cds/cds_globals.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/filemap.hpp"
+#include "cds/heapShared.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/javaAssertions.hpp"
 #include "classfile/moduleEntry.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
@@ -2948,14 +2949,18 @@
 
   if (!check_vm_args_consistency()) {
     return JNI_ERR;
   }
 
-  if (!CDSConfig::check_vm_args_consistency(patch_mod_javabase, mode_flag_cmd_line)) {
+  if (!CDSConfig::check_vm_args_consistency(patch_mod_javabase, mode_flag_cmd_line, xshare_auto_cmd_line)) {
     return JNI_ERR;
   }
 
+  if (StoreCachedCode) {
+    FLAG_SET_ERGO_IF_DEFAULT(CachedCodeMaxSize, 512*M);
+  }
+
 #ifndef CAN_SHOW_REGISTERS_ON_ASSERT
   UNSUPPORTED_OPTION(ShowRegistersOnAssert);
 #endif // CAN_SHOW_REGISTERS_ON_ASSERT
 
   return JNI_OK;
@@ -3724,10 +3729,11 @@
     FLAG_SET_DEFAULT(UseSecondarySupersTable, VM_Version::supports_secondary_supers_table());
   } else if (UseSecondarySupersTable && !VM_Version::supports_secondary_supers_table()) {
     warning("UseSecondarySupersTable is not supported");
     FLAG_SET_DEFAULT(UseSecondarySupersTable, false);
   }
+  UseSecondarySupersTable = false; // FIXME: Disabled for Leyden. Neet to fix SCAddressTable::id_for_address()
   if (!UseSecondarySupersTable) {
     FLAG_SET_DEFAULT(StressSecondarySupers, false);
     FLAG_SET_DEFAULT(VerifySecondarySupers, false);
   }
 
@@ -3785,16 +3791,42 @@
     }
     FLAG_SET_DEFAULT(UseVectorStubs, false);
   }
 #endif // COMPILER2_OR_JVMCI
 
-  if (log_is_enabled(Info, perf, class, link)) {
-    if (!UsePerfData) {
-      warning("Disabling -Xlog:perf+class+link since UsePerfData is turned off.");
+  if (log_is_enabled(Info, init)) {
+    if (FLAG_IS_DEFAULT(ProfileVMLocks)) {
+      FLAG_SET_DEFAULT(ProfileVMLocks, true);
+    }
+    if (UsePerfData && !log_is_enabled(Info, perf, class, link)) {
+      // automatically enable -Xlog:perf+class+link
+      LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(perf, class, link));
+    }
+    // Don't turn on ProfileVMCalls and ProfileRuntimeCalls by default.
+  } else {
+    if (!FLAG_IS_DEFAULT(ProfileVMLocks) && ProfileVMLocks) {
+      warning("Disabling ProfileVMLocks since logging is turned off.");
+      FLAG_SET_DEFAULT(ProfileVMLocks, false);
+    }
+    if (!FLAG_IS_DEFAULT(ProfileVMCalls) && ProfileVMCalls) {
+      warning("Disabling ProfileVMCalls since logging is turned off.");
+      FLAG_SET_DEFAULT(ProfileVMCalls, false);
+    }
+    if (!FLAG_IS_DEFAULT(ProfileRuntimeCalls) && ProfileRuntimeCalls) {
+      warning("Disabling ProfileRuntimeCalls since logging is turned off.");
+      FLAG_SET_DEFAULT(ProfileRuntimeCalls, false);
+    }
+    if (log_is_enabled(Info, perf, class, link)) {
+      warning("Disabling -Xlog:perf+class+link since logging is turned off.");
       LogConfiguration::configure_stdout(LogLevel::Off, false, LOG_TAGS(perf, class, link));
     }
   }
+  if (FLAG_IS_DEFAULT(PerfDataMemorySize)) {
+    if (ProfileVMLocks || ProfileVMCalls || ProfileRuntimeCalls) {
+      FLAG_SET_DEFAULT(PerfDataMemorySize, 128*K); // reserve more space for extra perf counters
+    }
+  }
 
   if (FLAG_IS_CMDLINE(DiagnoseSyncOnValueBasedClasses)) {
     if (DiagnoseSyncOnValueBasedClasses == ObjectSynchronizer::LOG_WARNING && !log_is_enabled(Info, valuebasedclasses)) {
       LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(valuebasedclasses));
     }
diff a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp
--- a/src/hotspot/share/runtime/deoptimization.cpp
+++ b/src/hotspot/share/runtime/deoptimization.cpp
@@ -78,10 +78,11 @@
 #include "runtime/keepStackGCProcessed.hpp"
 #include "runtime/lightweightSynchronizer.hpp"
 #include "runtime/lockStack.inline.hpp"
 #include "runtime/objectMonitor.inline.hpp"
 #include "runtime/osThread.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/safepointVerifiers.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/stackFrameStream.inline.hpp"
 #include "runtime/stackValue.hpp"
@@ -92,10 +93,11 @@
 #include "runtime/threadWXSetters.inline.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vframe_hp.hpp"
 #include "runtime/vmOperations.hpp"
+#include "services/management.hpp"
 #include "utilities/checkedCast.hpp"
 #include "utilities/events.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/preserveException.hpp"
@@ -279,11 +281,11 @@
 
 // In order to make fetch_unroll_info work properly with escape
 // analysis, the method was changed from JRT_LEAF to JRT_BLOCK_ENTRY.
 // The actual reallocation of previously eliminated objects occurs in realloc_objects,
 // which is called from the method fetch_unroll_info_helper below.
-JRT_BLOCK_ENTRY(Deoptimization::UnrollBlock*, Deoptimization::fetch_unroll_info(JavaThread* current, int exec_mode))
+JRT_BLOCK_ENTRY_PROF(Deoptimization::UnrollBlock*, Deoptimization, fetch_unroll_info, Deoptimization::fetch_unroll_info(JavaThread* current, int exec_mode))
   // fetch_unroll_info() is called at the beginning of the deoptimization
   // handler. Note this fact before we start generating temporary frames
   // that can confuse an asynchronous stack walker. This counter is
   // decremented at the end of unpack_frames().
   current->inc_in_deopt_handler();
@@ -851,11 +853,11 @@
   }
 }
 #endif
 
 // Return BasicType of value being returned
-JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_mode))
+JRT_LEAF_PROF_NO_THREAD(BasicType, Deoptimization, unpack_frames, Deoptimization::unpack_frames(JavaThread* thread, int exec_mode))
   assert(thread == JavaThread::current(), "pre-condition");
 
   // We are already active in the special DeoptResourceMark any ResourceObj's we
   // allocate will be freed at the end of the routine.
 
@@ -1762,16 +1764,19 @@
 #endif
 
 void Deoptimization::deoptimize_single_frame(JavaThread* thread, frame fr, Deoptimization::DeoptReason reason) {
   assert(fr.can_be_deoptimized(), "checking frame type");
 
-  gather_statistics(reason, Action_none, Bytecodes::_illegal);
+  nmethod* nm = fr.cb()->as_nmethod_or_null();
+  assert(nm != nullptr, "only compiled methods can deopt");
+  DeoptAction action = (nm->is_not_entrant() ? Action_make_not_entrant : Action_none);
+  ScopeDesc* cur_sd = nm->scope_desc_at(fr.pc());
+  Bytecodes::Code bc = (cur_sd->bci() == -1 ? Bytecodes::_nop // deopt on method entry
+                                            : cur_sd->method()->java_code_at(cur_sd->bci()));
+  gather_statistics(nm, reason, action, bc);
 
   if (LogCompilation && xtty != nullptr) {
-    nmethod* nm = fr.cb()->as_nmethod_or_null();
-    assert(nm != nullptr, "only compiled methods can deopt");
-
     ttyLocker ttyl;
     xtty->begin_head("deoptimized thread='" UINTX_FORMAT "' reason='%s' pc='" INTPTR_FORMAT "'",(uintx)thread->osthread()->thread_id(), trap_reason_name(reason), p2i(fr.pc()));
     nm->log_identity(xtty);
     xtty->end_head();
     for (ScopeDesc* sd = nm->scope_desc_at(fr.pc()); ; sd = sd->sender()) {
@@ -2017,11 +2022,11 @@
     ls.print_cr("pc=" INTPTR_FORMAT " relative_pc=" INTPTR_FORMAT,
              pc, fr.pc() - nm->code_begin());
   }
 }
 
-JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint trap_request)) {
+JRT_ENTRY_PROF(void, Deoptimization, uncommon_trap_inner, Deoptimization::uncommon_trap_inner(JavaThread* current, jint trap_request)) {
   HandleMark hm(current);
 
   // uncommon_trap() is called at the beginning of the uncommon trap
   // handler. Note this fact before we start generating temporary frames
   // that can confuse an asynchronous stack walker. This counter is
@@ -2093,11 +2098,11 @@
     }
 #endif
 
     Bytecodes::Code trap_bc     = trap_method->java_code_at(trap_bci);
     // Record this event in the histogram.
-    gather_statistics(reason, action, trap_bc);
+    gather_statistics(nm, reason, action, trap_bc);
 
     // Ensure that we can record deopt. history:
     bool create_if_missing = ProfileTraps;
 
     methodHandle profiled_method;
@@ -2605,22 +2610,22 @@
                            ignore_this_trap_count,
                            ignore_maybe_prior_trap,
                            ignore_maybe_prior_recompile);
 }
 
-Deoptimization::UnrollBlock* Deoptimization::uncommon_trap(JavaThread* current, jint trap_request, jint exec_mode) {
+PROF_ENTRY(Deoptimization::UnrollBlock*, Deoptimization, uncommon_trap, Deoptimization::uncommon_trap(JavaThread* current, jint trap_request, jint exec_mode))
   // Enable WXWrite: current function is called from methods compiled by C2 directly
   MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, current));
 
   // Still in Java no safepoints
   {
     // This enters VM and may safepoint
     uncommon_trap_inner(current, trap_request);
   }
   HandleMark hm(current);
   return fetch_unroll_info_helper(current, exec_mode);
-}
+PROF_END
 
 // Local derived constants.
 // Further breakdown of DataLayout::trap_state, as promised by DataLayout.
 const int DS_REASON_MASK   = ((uint)DataLayout::trap_mask) >> 1;
 const int DS_RECOMPILE_BIT = DataLayout::trap_mask - DS_REASON_MASK;
@@ -2805,32 +2810,27 @@
   }
   return buf;
 }
 
 juint Deoptimization::_deoptimization_hist
+        [1 + 4 + 5] // total + online + archived
         [Deoptimization::Reason_LIMIT]
     [1 + Deoptimization::Action_LIMIT]
         [Deoptimization::BC_CASE_LIMIT]
   = {0};
 
 enum {
   LSB_BITS = 8,
   LSB_MASK = right_n_bits(LSB_BITS)
 };
 
-void Deoptimization::gather_statistics(DeoptReason reason, DeoptAction action,
-                                       Bytecodes::Code bc) {
-  assert(reason >= 0 && reason < Reason_LIMIT, "oob");
-  assert(action >= 0 && action < Action_LIMIT, "oob");
-  _deoptimization_hist[Reason_none][0][0] += 1;  // total
-  _deoptimization_hist[reason][0][0]      += 1;  // per-reason total
-  juint* cases = _deoptimization_hist[reason][1+action];
+static void update(juint* cases, Bytecodes::Code bc) {
   juint* bc_counter_addr = nullptr;
   juint  bc_counter      = 0;
   // Look for an unused counter, or an exact match to this BC.
   if (bc != Bytecodes::_illegal) {
-    for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) {
+    for (int bc_case = 0; bc_case < Deoptimization::BC_CASE_LIMIT; bc_case++) {
       juint* counter_addr = &cases[bc_case];
       juint  counter = *counter_addr;
       if ((counter == 0 && bc_counter_addr == nullptr)
           || (Bytecodes::Code)(counter & LSB_MASK) == bc) {
         // this counter is either free or is already devoted to this BC
@@ -2839,18 +2839,34 @@
       }
     }
   }
   if (bc_counter_addr == nullptr) {
     // Overflow, or no given bytecode.
-    bc_counter_addr = &cases[BC_CASE_LIMIT-1];
+    bc_counter_addr = &cases[Deoptimization::BC_CASE_LIMIT-1];
     bc_counter = (*bc_counter_addr & ~LSB_MASK);  // clear LSB
   }
   *bc_counter_addr = bc_counter + (1 << LSB_BITS);
 }
 
+
+void Deoptimization::gather_statistics(nmethod* nm, DeoptReason reason, DeoptAction action,
+                                       Bytecodes::Code bc) {
+  assert(reason >= 0 && reason < Reason_LIMIT, "oob");
+  assert(action >= 0 && action < Action_LIMIT, "oob");
+  _deoptimization_hist[0][Reason_none][0][0] += 1;  // total
+  _deoptimization_hist[0][reason][0][0]      += 1;  // per-reason total
+
+  update(_deoptimization_hist[0][reason][1+action], bc);
+
+  uint lvl = nm->comp_level() + (nm->is_scc() ? 4 : 0) + (nm->preloaded() ? 1 : 0);
+  _deoptimization_hist[lvl][Reason_none][0][0] += 1;  // total
+  _deoptimization_hist[lvl][reason][0][0]      += 1;  // per-reason total
+  update(_deoptimization_hist[lvl][reason][1+action], bc);
+}
+
 jint Deoptimization::total_deoptimization_count() {
-  return _deoptimization_hist[Reason_none][0][0];
+  return _deoptimization_hist[0][Reason_none][0][0];
 }
 
 // Get the deopt count for a specific reason and a specific action. If either
 // one of 'reason' or 'action' is null, the method returns the sum of all
 // deoptimizations with the specific 'action' or 'reason' respectively.
@@ -2862,11 +2878,11 @@
   juint counter = 0;
   for (int reason = 0; reason < Reason_LIMIT; reason++) {
     if (reason_str == nullptr || !strcmp(reason_str, trap_reason_name(reason))) {
       for (int action = 0; action < Action_LIMIT; action++) {
         if (action_str == nullptr || !strcmp(action_str, trap_action_name(action))) {
-          juint* cases = _deoptimization_hist[reason][1+action];
+          juint* cases = _deoptimization_hist[0][reason][1+action];
           for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) {
             counter += cases[bc_case] >> LSB_BITS;
           }
         }
       }
@@ -2874,50 +2890,112 @@
   }
   return counter;
 }
 
 void Deoptimization::print_statistics() {
-  juint total = total_deoptimization_count();
+  ttyLocker ttyl;
+  if (xtty != nullptr)  xtty->head("statistics type='deoptimization'");
+  tty->print_cr("Deoptimization traps recorded:");
+  print_statistics_on(tty);
+  if (xtty != nullptr)  xtty->tail("statistics");
+}
+
+void Deoptimization::print_statistics_on(const char* title, int lvl, outputStream* st) {
+  juint total = _deoptimization_hist[lvl][Reason_none][0][0];
   juint account = total;
-  if (total != 0) {
-    ttyLocker ttyl;
-    if (xtty != nullptr)  xtty->head("statistics type='deoptimization'");
-    tty->print_cr("Deoptimization traps recorded:");
-    #define PRINT_STAT_LINE(name, r) \
-      tty->print_cr("  %4d (%4.1f%%) %s", (int)(r), ((r) * 100.0) / total, name);
+#define PRINT_STAT_LINE(name, r) \
+      st->print_cr("    %d (%4.1f%%) %s", (int)(r), ((r) == total ? 100.0 : (((r) * 100.0) / total)), name);
+  if (total > 0) {
+    st->print("  %s: ", title);
     PRINT_STAT_LINE("total", total);
     // For each non-zero entry in the histogram, print the reason,
     // the action, and (if specifically known) the type of bytecode.
     for (int reason = 0; reason < Reason_LIMIT; reason++) {
       for (int action = 0; action < Action_LIMIT; action++) {
-        juint* cases = _deoptimization_hist[reason][1+action];
+        juint* cases = Deoptimization::_deoptimization_hist[lvl][reason][1+action];
         for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) {
           juint counter = cases[bc_case];
           if (counter != 0) {
-            char name[1*K];
             Bytecodes::Code bc = (Bytecodes::Code)(counter & LSB_MASK);
-            if (bc_case == BC_CASE_LIMIT && (int)bc == 0)
-              bc = Bytecodes::_illegal;
-            os::snprintf_checked(name, sizeof(name), "%s/%s/%s",
-                    trap_reason_name(reason),
-                    trap_action_name(action),
-                    Bytecodes::is_defined(bc)? Bytecodes::name(bc): "other");
+            const char* bc_name = "other";
+            if (bc_case == (BC_CASE_LIMIT-1) && bc == Bytecodes::_nop) {
+              // overwritten
+            } else if (Bytecodes::is_defined(bc)) {
+              bc_name = Bytecodes::name(bc);
+            }
             juint r = counter >> LSB_BITS;
-            tty->print_cr("  %40s: " UINT32_FORMAT " (%.1f%%)", name, r, (r * 100.0) / total);
+            st->print_cr("    %-34s %16s %16s: " UINT32_FORMAT_W(5) " (%4.1f%%)",
+                         trap_reason_name(reason), trap_action_name(action), bc_name,
+                         r, (r * 100.0) / total);
             account -= r;
           }
         }
       }
     }
     if (account != 0) {
       PRINT_STAT_LINE("unaccounted", account);
     }
-    #undef PRINT_STAT_LINE
-    if (xtty != nullptr)  xtty->tail("statistics");
+#undef PRINT_STAT_LINE
   }
 }
 
+void Deoptimization::print_statistics_on(outputStream* st) {
+//  print_statistics_on("Total", 0, st);
+  print_statistics_on("Tier1", 1, st);
+  print_statistics_on("Tier2", 2, st);
+  print_statistics_on("Tier3", 3, st);
+  print_statistics_on("Tier4", 4, st);
+
+  print_statistics_on("SC Tier1", 5, st);
+  print_statistics_on("SC Tier2", 6, st);
+  print_statistics_on("SC Tier4", 8, st);
+  print_statistics_on("SC Tier5 (preloaded)", 9, st);
+}
+
+#define DO_COUNTERS(macro) \
+  macro(Deoptimization, fetch_unroll_info)   \
+  macro(Deoptimization, unpack_frames) \
+  macro(Deoptimization, uncommon_trap_inner) \
+  macro(Deoptimization, uncommon_trap)
+
+#define INIT_COUNTER(sub, name) \
+  NEWPERFTICKCOUNTERS(_perf_##sub##_##name##_timer, SUN_RT, #sub "::" #name) \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_RT, #sub "::" #name "_count");
+
+void Deoptimization::init_counters() {
+  if (ProfileRuntimeCalls && UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS(INIT_COUNTER)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("jvm_perf_init failed unexpectedly");
+    }
+  }
+}
+#undef INIT_COUNTER
+
+#define PRINT_COUNTER(sub, name) { \
+  jlong count = _perf_##sub##_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-50s = " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) (" JLONG_FORMAT_W(5) " events)", #sub "::" #name, \
+                 _perf_##sub##_##name##_timer->elapsed_counter_value_us(), \
+                 _perf_##sub##_##name##_timer->thread_counter_value_us(), \
+                 count); \
+  }}
+
+void Deoptimization::print_counters_on(outputStream* st) {
+  if (ProfileRuntimeCalls && UsePerfData) {
+    DO_COUNTERS(PRINT_COUNTER)
+  } else {
+    st->print_cr("  Deoptimization: no info (%s is disabled)", (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData"));
+  }
+}
+
+#undef PRINT_COUNTER
+#undef DO_COUNTERS
+
 #else // COMPILER2_OR_JVMCI
 
 
 // Stubs for C1 only system.
 bool Deoptimization::trap_state_is_recompiled(int trap_state) {
@@ -2947,17 +3025,29 @@
 
 int Deoptimization::trap_state_has_reason(int trap_state, int reason) {
   return 0;
 }
 
-void Deoptimization::gather_statistics(DeoptReason reason, DeoptAction action,
+void Deoptimization::gather_statistics(nmethod* nm, DeoptReason reason, DeoptAction action,
                                        Bytecodes::Code bc) {
   // no update
 }
 
 const char* Deoptimization::format_trap_state(char* buf, size_t buflen,
                                               int trap_state) {
   jio_snprintf(buf, buflen, "#%d", trap_state);
   return buf;
 }
 
+void Deoptimization::init_counters() {
+  // nothing to do
+}
+
+void Deoptimization::print_counters_on(outputStream* st) {
+  // no output
+}
+
+void Deoptimization::print_statistics_on(outputStream* st) {
+  // no output
+}
+
 #endif // COMPILER2_OR_JVMCI
diff a/src/hotspot/share/runtime/deoptimization.hpp b/src/hotspot/share/runtime/deoptimization.hpp
--- a/src/hotspot/share/runtime/deoptimization.hpp
+++ b/src/hotspot/share/runtime/deoptimization.hpp
@@ -154,11 +154,11 @@
     _reason_bits = 5,
     _debug_id_bits = 23,
     _action_shift = 0,
     _reason_shift = _action_shift+_action_bits,
     _debug_id_shift = _reason_shift+_reason_bits,
-    BC_CASE_LIMIT = PRODUCT_ONLY(1) NOT_PRODUCT(4) // for _deoptimization_hist
+    BC_CASE_LIMIT = 4 // for _deoptimization_hist
   };
 
   enum UnpackType {
     Unpack_deopt                = 0, // normal deoptimization, use pc computed in unpack_vframe_on_stack
     Unpack_exception            = 1, // exception is pending
@@ -323,13 +323,16 @@
   // VM_DeoptimizeFrame otherwise deoptimize directly.
   static void deoptimize_frame(JavaThread* thread, intptr_t* id, DeoptReason reason);
   static void deoptimize_frame(JavaThread* thread, intptr_t* id);
 
   // Statistics
-  static void gather_statistics(DeoptReason reason, DeoptAction action,
+  static void gather_statistics(nmethod* nm, DeoptReason reason, DeoptAction action,
                                 Bytecodes::Code bc = Bytecodes::_illegal);
   static void print_statistics();
+  static void print_statistics_on(outputStream* st);
+
+  static void print_statistics_on(const char* title, int lvl, outputStream* st);
 
   // How much room to adjust the last frame's SP by, to make space for
   // the callee's interpreter frame (which expects locals to be next to
   // incoming arguments)
   static int last_frame_adjust(int callee_parameters, int callee_locals);
@@ -481,13 +484,16 @@
 
   static DeoptAction _unloaded_action; // == Action_reinterpret;
   static const char* _trap_reason_name[];
   static const char* _trap_action_name[];
 
-  static juint _deoptimization_hist[Reason_LIMIT][1+Action_LIMIT][BC_CASE_LIMIT];
+  static juint _deoptimization_hist[1 + 4 + 5][Reason_LIMIT][1+Action_LIMIT][BC_CASE_LIMIT];
   // Note:  Histogram array size is 1-2 Kb.
 
  public:
   static void update_method_data_from_interpreter(MethodData* trap_mdo, int trap_bci, int reason);
+
+  static void init_counters();
+  static void print_counters_on(outputStream* st);
 };
 
 #endif // SHARE_RUNTIME_DEOPTIMIZATION_HPP
diff a/src/hotspot/share/runtime/flags/jvmFlag.cpp b/src/hotspot/share/runtime/flags/jvmFlag.cpp
--- a/src/hotspot/share/runtime/flags/jvmFlag.cpp
+++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "jvm_io.h"
 #include "memory/allocation.inline.hpp"
+#include "memory/universe.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/flags/jvmFlag.hpp"
 #include "runtime/flags/jvmFlagAccess.hpp"
 #include "runtime/flags/jvmFlagLookup.hpp"
 #include "runtime/globals_extension.hpp"
@@ -693,10 +694,15 @@
   //       called as part of error reporting, so handle native OOMs gracefully.
 
   // The last entry is the null entry.
   constexpr size_t length = (sizeof(flagTable) / sizeof(JVMFlag)) - 1;
 
+  const char* tag = 0;
+  if (xtty_owns(out))
+    xtty->head("%s", tag = !Universe::is_fully_initialized()
+               ? "vm_flags_initial" : "vm_flags_final");
+
   // Print
   if (!printRanges) {
     out->print_cr("[Global flags]");
   } else {
     out->print_cr("[Global flags ranges]");
@@ -722,10 +728,12 @@
     if (bestFlag != nullptr) {
       bestFlag->print_on(out, withComments, printRanges);
       iteratorMarkers.at_put(bestFlagIndex, true);
     }
   }
+  if (xtty_owns(out))
+    xtty->tail(tag);
 }
 
 void JVMFlag::printError(bool verbose, const char* msg, ...) {
   if (verbose) {
     va_list listPointer;
diff a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -126,10 +126,15 @@
                                                                             \
   product(bool, UseCompressedClassPointers, true,                           \
           "Use 32-bit class pointers in 64-bit VM. "                        \
           "lp64_product means flag is always constant in 32 bit VM")        \
                                                                             \
+  /* Leyden */                                                              \
+  product(bool, UseCompatibleCompressedOops, false,                         \
+          "Always use HeapBasedNarrowOop mode, so that AOT code can be "    \
+          "always work regardless of runtime heap range")                   \
+                                                                            \
   product(bool, UseCompactObjectHeaders, false, EXPERIMENTAL,               \
           "Use compact 64-bit object headers in 64-bit VM")                 \
                                                                             \
   product(int, ObjectAlignmentInBytes, 8,                                   \
           "Default object alignment in bytes, 8 is minimum")                \
@@ -375,10 +380,16 @@
   product(ccstrlist, ControlIntrinsic, "", DIAGNOSTIC,                      \
          "Control intrinsics using a list of +/- (internal) names, "        \
          "separated by commas")                                             \
          constraint(ControlIntrinsicConstraintFunc,AfterErgo)               \
                                                                             \
+  develop(ccstr, AddRuntimeUpcallsNOP, nullptr,                             \
+          "Register a runtime upcall for testing."                          \
+          "Format is '[upcallType]:[methodFilter]'"                         \
+          "where upcallType is one of 'onMethodEntry', 'onMethodExit'"      \
+          "and methodFilter is one of 'none', 'all'")                       \
+                                                                            \
   develop(bool, TraceCallFixup, false,                                      \
           "Trace all call fixups")                                          \
                                                                             \
   develop(bool, DeoptimizeALot, false,                                      \
           "Deoptimize at every exit from the runtime system")               \
@@ -1011,15 +1022,15 @@
   product(bool, LogVMOutput, false, DIAGNOSTIC,                             \
           "Save VM output to LogFile")                                      \
                                                                             \
   product(ccstr, LogFile, nullptr, DIAGNOSTIC,                              \
           "If LogVMOutput or LogCompilation is on, save VM output to "      \
-          "this file [default: ./hotspot_pid%p.log] (%p replaced with pid)")\
+          "this file [default: ./hotspot_%p.log] (%p replaced with pidNNN)")\
                                                                             \
   product(ccstr, ErrorFile, nullptr,                                        \
           "If an error occurs, save the error data to this file "           \
-          "[default: ./hs_err_pid%p.log] (%p replaced with pid)")           \
+          "[default: ./hs_err_%p.log] (%p replaced with pidNNN)")           \
                                                                             \
   product(bool, ExtensiveErrorReports,                                      \
           PRODUCT_ONLY(false) NOT_PRODUCT(true),                            \
           "Error reports are more extensive.")                              \
                                                                             \
@@ -1135,16 +1146,23 @@
                                                                             \
   product_pd(bool, PreferInterpreterNativeStubs,                            \
           "Use always interpreter stubs for native methods invoked via "    \
           "interpreter")                                                    \
                                                                             \
-  develop(bool, CountBytecodes, false,                                      \
+  product(bool, CountBytecodes, false, DIAGNOSTIC,                          \
           "Count number of bytecodes executed")                             \
                                                                             \
-  develop(bool, PrintBytecodeHistogram, false,                              \
+  product(bool, CountBytecodesPerThread, false, DIAGNOSTIC,                 \
+          "Count number of bytecodes executed per thread")                  \
+                                                                            \
+  product(bool, PrintBytecodeHistogram, false, DIAGNOSTIC,                  \
           "Print histogram of the executed bytecodes")                      \
                                                                             \
+  product(double, PrintBytecodeHistogramCutoff, 0.01,                       \
+          "Print cutoff for histogram of the executed bytecodes")           \
+          range(0.0, 100.0)                                                 \
+                                                                            \
   develop(bool, PrintBytecodePairHistogram, false,                          \
           "Print histogram of the executed bytecode pairs")                 \
                                                                             \
   product(bool, PrintSignatureHandlers, false, DIAGNOSTIC,                  \
           "Print code generated for native method signature handlers")      \
@@ -1180,10 +1198,13 @@
   product(intx, ProfileMaturityPercentage, 20,                              \
           "number of method invocations/branches (expressed as % of "       \
           "CompileThreshold) before using the method's profile")            \
           range(0, 100)                                                     \
                                                                             \
+  product(bool, ForceProfiling, false, DIAGNOSTIC,                          \
+          "Eagerly allocate MDOs")                                          \
+                                                                            \
   product(bool, PrintMethodData, false, DIAGNOSTIC,                         \
           "Print the results of +ProfileInterpreter at end of run")         \
                                                                             \
   develop(bool, VerifyDataPointer, trueInDebug,                             \
           "Verify the method data pointer during interpreter profiling")    \
@@ -1717,11 +1738,11 @@
           constraint(PerfDataSamplingIntervalFunc, AfterErgo)               \
                                                                             \
   product(bool, PerfDisableSharedMem, false,                                \
           "Store performance data in standard memory")                      \
                                                                             \
-  product(int, PerfDataMemorySize, 32*K,                                    \
+  product(int, PerfDataMemorySize, 64*K,                                    \
           "Size of performance data memory region. Will be rounded "        \
           "up to a multiple of the native os page size.")                   \
           range(128, 32*64*K)                                               \
                                                                             \
   product(int, PerfMaxStringConstLength, 1024,                              \
@@ -1964,10 +1985,22 @@
           "the platform supports that. Lower values will reclaim memory "   \
           "more eagerly at the cost of higher overhead. A value of 0 "      \
           "(default) disables native heap trimming.")                       \
           range(0, UINT_MAX)                                                \
                                                                             \
+  product(bool, ProfileVMLocks, false, DIAGNOSTIC,                          \
+          "Profile VM locks usage on main thread")                          \
+                                                                            \
+  product(bool, ProfileVMCalls, false, DIAGNOSTIC,                          \
+          "Profile VM calls on main thread")                                \
+                                                                            \
+  product(bool, ProfileVMOps, true, DIAGNOSTIC,                             \
+          "Profile VM operations on main thread")                           \
+                                                                            \
+  product(bool, ProfileRuntimeCalls, false, DIAGNOSTIC,                     \
+          "Profile calls into VM runtime on main thread")                   \
+                                                                            \
   develop(bool, SimulateFullAddressSpace, false,                            \
           "Simulates a very populated, fragmented address space; no "       \
           "targeted reservations will succeed.")                            \
                                                                             \
   product(bool, ProfileExceptionHandlers, true,                             \
@@ -1976,10 +2009,13 @@
   product(bool, AlwaysRecordEvolDependencies, true, EXPERIMENTAL,           \
                 "Unconditionally record nmethod dependencies on class "     \
                 "rewriting/transformation independently of the JVMTI "      \
                 "can_{retransform/redefine}_classes capabilities.")         \
                                                                             \
+  product(bool, TraceThreadTime, false,                                     \
+          "Enable tracing of thread time in Perf counters")                 \
+                                                                            \
   product(bool, UseSecondarySupersCache, true, DIAGNOSTIC,                  \
                 "Use secondary supers cache during subtype checks.")        \
                                                                             \
   product(bool, UseSecondarySupersTable, false, DIAGNOSTIC,                 \
                 "Use hash table to lookup secondary supers.")               \
@@ -1992,10 +2028,11 @@
                                                                             \
   product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC,               \
           "Use an extra lock during Thread start and exit to alleviate"     \
           "contention on Threads_lock.")                                    \
 
+
 // end of RUNTIME_FLAGS
 
 DECLARE_FLAGS(LP64_RUNTIME_FLAGS)
 DECLARE_ARCH_FLAGS(ARCH_FLAGS)
 DECLARE_FLAGS(RUNTIME_FLAGS)
diff a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp
--- a/src/hotspot/share/runtime/init.cpp
+++ b/src/hotspot/share/runtime/init.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, 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.
@@ -21,19 +21,23 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/metaspaceShared.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compiler_globals.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gcHeapSummary.hpp"
 #include "interpreter/bytecodes.hpp"
 #include "logging/logAsyncWriter.hpp"
 #include "memory/universe.hpp"
 #include "nmt/memTracker.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/downcallLinker.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/continuation.hpp"
@@ -44,10 +48,11 @@
 #include "runtime/init.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "sanitizers/leak.hpp"
 #include "utilities/macros.hpp"
+#include "utilities/xmlstream.hpp"
 #if INCLUDE_JVMCI
 #include "jvmci/jvmci.hpp"
 #endif
 
 // Initialization done by VM thread in vm_init_globals()
@@ -86,10 +91,18 @@
 bool compilerOracle_init();
 bool compileBroker_init();
 void dependencyContext_init();
 void dependencies_init();
 
+// initialize upcalls before class loading
+bool runtimeUpcalls_open_registration();
+bool runtimeUpcallNop_register_upcalls();
+#if INCLUDE_CDS
+bool cdsEndTrainingUpcall_register_upcalls();
+#endif // INCLUDE_CDS
+void runtimeUpcalls_close_registration();
+
 // Initialization after compiler initialization
 bool universe_post_init();  // must happen after compiler_init
 void javaClasses_init();    // must happen after vtable initialization
 void compiler_stubs_init(bool in_compiler_thread); // compiler's StubRoutines stubs
 void final_stubs_init();    // final StubRoutines stubs
@@ -98,10 +111,12 @@
 // JNI/JVM/JVMTI functions and signal handlers to work properly
 // during VM shutdown
 void perfMemory_exit();
 void ostream_exit();
 
+void perf_jvm_init();
+
 void vm_init_globals() {
   check_ThreadShadow();
   basic_types_init();
   eventlog_init();
   mutex_init();
@@ -111,10 +126,13 @@
   ExternalsRecorder_init(); // After mutex_init() and before CodeCache_init
 }
 
 
 jint init_globals() {
+  perf_jvm_init();
+  MethodHandles::init_counters();
+
   management_init();
   JvmtiExport::initialize_oop_storage();
 #if INCLUDE_JVMTI
   if (AlwaysRecordEvolDependencies) {
     JvmtiExport::set_can_hotswap_or_post_breakpoint(true);
@@ -122,29 +140,30 @@
   }
 #endif
   bytecodes_init();
   classLoader_init1();
   compilationPolicy_init();
+  MetaspaceShared::open_static_archive();
   codeCache_init();
   VM_Version_init();              // depends on codeCache_init for emitting code
   // stub routines in initial blob are referenced by later generated code
   initial_stubs_init();
   // stack overflow exception blob is referenced by the interpreter
   SharedRuntime::generate_initial_stubs();
   jint status = universe_init();  // dependent on codeCache_init and
                                   // initial_stubs_init and metaspace_init.
   if (status != JNI_OK)
     return status;
-
+  SCCache::initialize();
 #ifdef LEAK_SANITIZER
   {
     // Register the Java heap with LSan.
     VirtualSpaceSummary summary = Universe::heap()->create_heap_space_summary();
     LSAN_REGISTER_ROOT_REGION(summary.start(), summary.reserved_size());
   }
 #endif // LEAK_SANITIZER
-
+  SCCache::init2();        // depends on universe_init
   AsyncLogWriter::initialize();
   gc_barrier_stubs_init();   // depends on universe_init, must be before interpreter_init
   continuations_init();      // must precede continuation stub generation
   continuation_stubs_init(); // depends on continuations_init
 #if INCLUDE_JFR
@@ -158,10 +177,23 @@
   return JNI_OK;
 }
 
 jint init_globals2() {
   universe2_init();          // dependent on codeCache_init and initial_stubs_init
+
+  // initialize upcalls before class loading / initialization
+  runtimeUpcalls_open_registration();
+  if (!runtimeUpcallNop_register_upcalls()) {
+    return JNI_EINVAL;
+  }
+#if INCLUDE_CDS
+  if (!cdsEndTrainingUpcall_register_upcalls()) {
+    return JNI_EINVAL;
+  }
+#endif // INCLUDE_CDS
+  runtimeUpcalls_close_registration();
+
   javaClasses_init();        // must happen after vtable initialization, before referenceProcessor_init
   interpreter_init_code();   // after javaClasses_init and before any method gets linked
   referenceProcessor_init();
   jni_handles_init();
 #if INCLUDE_VM_STRUCTS
@@ -182,10 +214,14 @@
   if (EnableJVMCI) {
     JVMCI::initialize_globals();
   }
 #endif
 
+  if (TrainingData::have_data() || TrainingData::need_data()) {
+    TrainingData::initialize();
+  }
+
   if (!universe_post_init()) {
     return JNI_ERR;
   }
   compiler_stubs_init(false /* in_compiler_thread */); // compiler's intrinsics stubs
   final_stubs_init();    // final StubRoutines stubs
@@ -193,10 +229,12 @@
 
   // All the flags that get adjusted by VM_Version_init and os::init_2
   // have been set so dump the flags now.
   if (PrintFlagsFinal || PrintFlagsRanges) {
     JVMFlag::printFlags(tty, false, PrintFlagsRanges);
+  } else if (RecordTraining && xtty != nullptr) {
+    JVMFlag::printFlags(xtty->log_only(), false, PrintFlagsRanges);
   }
 
   return JNI_OK;
 }
 
diff a/src/hotspot/share/runtime/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp
--- a/src/hotspot/share/runtime/interfaceSupport.inline.hpp
+++ b/src/hotspot/share/runtime/interfaceSupport.inline.hpp
@@ -33,10 +33,11 @@
 #include "runtime/handles.inline.hpp"
 #include "runtime/javaThread.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/orderAccess.hpp"
 #include "runtime/os.hpp"
+#include "runtime/perfData.hpp"
 #include "runtime/safepointMechanism.inline.hpp"
 #include "runtime/safepointVerifiers.hpp"
 #include "runtime/threadWXSetters.inline.hpp"
 #include "runtime/vmOperations.hpp"
 #include "utilities/globalDefinitions.hpp"
@@ -336,13 +337,76 @@
     assert(current == JavaThread::current(), "Must be");             \
     ThreadInVMfromJava __tiv(current, false /* check asyncs */);     \
     JavaThread* THREAD = current; /* For exception macros. */        \
     debug_only(VMEntryWrapper __vew;)
 
-#define JRT_BLOCK_END }
+#define JRT_ENTRY_PROF(result_type, sub, name, header)               \
+  PerfTickCounters* _perf_##sub##_##name##_timer = nullptr;          \
+  PerfCounter* _perf_##sub##_##name##_count = nullptr;               \
+  result_type header {                                               \
+    assert(current == JavaThread::current(), "Must be");             \
+    PerfTraceTimedEvent perf_##sub##_##name(_perf_##sub##_##name##_timer, _perf_##sub##_##name##_count, current->do_profile_rt_call()); \
+    ProfileVMCallContext pctx(current, &(perf_##sub##_##name), current->do_profile_rt_call()); \
+    MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current));       \
+    ThreadInVMfromJava __tiv(current);                               \
+    VM_ENTRY_BASE(result_type, header, current)                      \
+    debug_only(VMEntryWrapper __vew;)
+
+#define JRT_LEAF_PROF(result_type, sub, name, header)                \
+  PerfTickCounters* _perf_##sub##_##name##_timer = nullptr;          \
+  PerfCounter* _perf_##sub##_##name##_count = nullptr;               \
+  result_type header {                                               \
+    PerfTraceTimedEvent perf_##sub##_##name(_perf_##sub##_##name##_timer, _perf_##sub##_##name##_count, \
+                                            current->do_profile_rt_call()); \
+    ProfileVMCallContext pctx(current, &(perf_##sub##_##name), current->do_profile_rt_call()); \
+    VM_LEAF_BASE(result_type, header)                                \
+    debug_only(NoSafepointVerifier __nsv;)
+
+#define JRT_LEAF_PROF_NO_THREAD(result_type, sub, name, header)      \
+  PerfTickCounters* _perf_##sub##_##name##_timer = nullptr;          \
+  PerfCounter* _perf_##sub##_##name##_count = nullptr;               \
+  result_type header {                                               \
+    Thread* current = Thread::current();                             \
+    PerfTraceTimedEvent perf_##sub##_##name(_perf_##sub##_##name##_timer, _perf_##sub##_##name##_count, \
+                                            current->do_profile_rt_call()); \
+    ProfileVMCallContext pctx(current, &(perf_##sub##_##name), current->do_profile_rt_call()); \
+    VM_LEAF_BASE(result_type, header)                                \
+    debug_only(NoSafepointVerifier __nsv;)
+
+#define JRT_ENTRY_NO_ASYNC_PROF(result_type, sub, name, header)      \
+  PerfTickCounters* _perf_##sub##_##name##_timer = nullptr;          \
+  PerfCounter* _perf_##sub##_##name##_count = nullptr;               \
+  result_type header {                                               \
+    assert(current == JavaThread::current(), "Must be");             \
+    PerfTraceTimedEvent perf_##sub##_##name(_perf_##sub##_##name##_timer, _perf_##sub##_##name##_count, current->do_profile_rt_call()); \
+    ProfileVMCallContext pctx(current, &(perf_##sub##_##name), current->do_profile_rt_call()); \
+    MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current));       \
+    ThreadInVMfromJava __tiv(current, false /* check asyncs */);     \
+    VM_ENTRY_BASE(result_type, header, current)                      \
+    debug_only(VMEntryWrapper __vew;)
+
+#define JRT_BLOCK_ENTRY_PROF(result_type, sub, name, header)         \
+  PerfTickCounters* _perf_##sub##_##name##_timer = nullptr;          \
+  PerfCounter* _perf_##sub##_##name##_count = nullptr;               \
+  result_type header {                                               \
+    assert(current == JavaThread::current(), "Must be");             \
+    PerfTraceTimedEvent perf_##sub##_##name(_perf_##sub##_##name##_timer, _perf_##sub##_##name##_count, current->do_profile_rt_call()); \
+    ProfileVMCallContext pctx(current, &(perf_##sub##_##name), current->do_profile_rt_call()); \
+    MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current));       \
+    HandleMarkCleaner __hm(current);
 
+#define PROF_ENTRY(result_type, sub, name, header)                   \
+  PerfTickCounters* _perf_##sub##_##name##_timer = nullptr;          \
+  PerfCounter* _perf_##sub##_##name##_count = nullptr;               \
+  result_type header {                                               \
+    assert(current == JavaThread::current(), "must be");             \
+    PerfTraceTimedEvent perf_##sub##_##name(_perf_##sub##_##name##_timer, _perf_##sub##_##name##_count, current->do_profile_rt_call()); \
+    ProfileVMCallContext pctx(current, &(perf_##sub##_##name), current->do_profile_rt_call());
+
+#define JRT_BLOCK_END }
 #define JRT_END }
+#define PROF_END }
 
 // Definitions for JNI
 //
 // As the JNIEnv can be passed from external native code we validate
 // it in debug builds, primarily for our own testing. In general JNI
@@ -404,8 +468,44 @@
   result_type JNICALL header {                                       \
     VM_Exit::block_if_vm_exited();                                   \
     VM_LEAF_BASE(result_type, header)
 
 
+#define JVM_ENTRY_PROF(result_type, name, header)                    \
+  PerfTickCounters* _perf_##name##_timer = nullptr;                  \
+  PerfCounter* _perf_##name##_count = nullptr;                       \
+extern "C" {                                                         \
+  result_type JNICALL header {                                       \
+    JavaThread* thread=JavaThread::thread_from_jni_environment(env); \
+    PerfTraceTimedEvent perf_##name(_perf_##name##_timer, _perf_##name##_count, thread->profile_vm_calls()); \
+    MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread));        \
+    ThreadInVMfromNative __tiv(thread);                              \
+    debug_only(VMNativeEntryWrapper __vew;)                          \
+    VM_ENTRY_BASE(result_type, header, thread)
+
+
+#define JVM_ENTRY_NO_ENV_PROF(result_type, name, header)             \
+  PerfTickCounters* _perf_##name##_timer = nullptr;                  \
+  PerfCounter* _perf_##name##_count = nullptr;                       \
+extern "C" {                                                         \
+  result_type JNICALL header {                                       \
+    JavaThread* thread = JavaThread::current();                      \
+    PerfTraceTimedEvent perf_##name(_perf_##name##_timer, _perf_##name##_count, thread->profile_vm_calls()); \
+    MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread));        \
+    ThreadInVMfromNative __tiv(thread);                              \
+    debug_only(VMNativeEntryWrapper __vew;)                          \
+    VM_ENTRY_BASE(result_type, header, thread)
+
+
+#define JVM_LEAF_PROF(result_type, name, header)                     \
+  PerfTickCounters* _perf_##name##_timer = nullptr;                  \
+  PerfCounter* _perf_##name##_count = nullptr;                       \
+extern "C" {                                                         \
+  result_type JNICALL header {                                       \
+    PerfTraceTimedEvent perf_##name(_perf_##name##_timer, _perf_##name##_count, \
+                                    ProfileVMCalls && Thread::current()->profile_vm_calls()); \
+    VM_Exit::block_if_vm_exited();                                   \
+    VM_LEAF_BASE(result_type, header)
+
 #define JVM_END } }
 
 #endif // SHARE_RUNTIME_INTERFACESUPPORT_INLINE_HPP
diff a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp
--- a/src/hotspot/share/runtime/java.cpp
+++ b/src/hotspot/share/runtime/java.cpp
@@ -21,26 +21,33 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotLinkedClassBulkLoader.hpp"
 #include "cds/cds_globals.hpp"
+#include "cds/cdsConfig.hpp"
 #include "cds/classListWriter.hpp"
 #include "cds/dynamicArchive.hpp"
+#include "cds/methodProfiler.hpp"
+#include "cds/metaspaceShared.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "code/codeCache.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilationMemoryStatistic.hpp"
+#include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compilerOracle.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/stringdedup/stringDedup.hpp"
 #include "interpreter/bytecodeHistogram.hpp"
+#include "interpreter/interpreterRuntime.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "jfr/support/jfrThreadId.hpp"
 #include "jvm.h"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
@@ -57,15 +64,18 @@
 #include "oops/klassVtable.hpp"
 #include "oops/method.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/symbol.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/jvmtiAgentList.hpp"
 #include "prims/jvmtiExport.hpp"
+#include "prims/methodHandles.hpp"
 #include "runtime/continuation.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/flags/flagSetting.hpp"
+#include "runtime/globals_extension.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/init.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaThread.hpp"
@@ -157,10 +167,92 @@
       tty->print_cr("Total MDO size: %d bytes", total_size);
     }
   }
 }
 
+void perf_jvm_print_on(outputStream* st);
+
+void log_vm_init_stats() {
+  LogStreamHandle(Info, init) log;
+  if (log.is_enabled()) {
+    SharedRuntime::print_counters_on(&log);
+    ClassLoader::print_counters(&log);
+    AOTLinkedClassBulkLoader::print_counters_on(&log);
+    log.cr();
+    // FIXME: intermittent crashes
+//    if (CountBytecodesPerThread) {
+//      log.print_cr("Thread info:");
+//      class PrintThreadInfo : public ThreadClosure {
+//        outputStream* _st;
+//      public:
+//        PrintThreadInfo(outputStream* st) : ThreadClosure(), _st(st) {}
+//        void do_thread(Thread* thread) {
+//          JavaThread* jt = JavaThread::cast(thread);
+//          if (jt->bc_counter_value() > 0) {
+//            _st->print_cr("  Thread " INTPTR_FORMAT ": %ld bytecodes executed (clinit: %ld)",
+//                          p2i(jt), /*jt->name(),*/ jt->bc_counter_value(), jt->clinit_bc_counter_value());
+//          }
+//        }
+//      };
+//      PrintThreadInfo cl(&log);
+//      Threads::java_threads_do(&cl);
+//    }
+//    log.cr();
+    log.print_cr("Deoptimization events: ");
+    Deoptimization::print_statistics_on(&log);
+    log.cr();
+
+    log.print("Compilation statistics: ");
+    CompileBroker::print_statistics_on(&log);
+    log.cr();
+
+    {
+      MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+      log.print("Code cache statistics: ");
+      CodeCache::print_nmethod_statistics_on(&log);
+      log.cr();
+    }
+
+    if (SCCache::is_on_for_read()) {
+      log.print_cr("Startup Code Cache: ");
+      SCCache::print_statistics_on(&log);
+      log.cr();
+      SCCache::print_timers_on(&log);
+    }
+
+    VMThread::print_counters_on(&log);
+    log.cr();
+    MutexLockerImpl::print_counters_on(&log);
+    log.cr();
+    log.print("Runtime events for thread \"main\"");
+    if (ProfileRuntimeCalls) {
+      log.print_cr(" (%d nested events):", ProfileVMCallContext::nested_runtime_calls_count());
+
+      InterpreterRuntime::print_counters_on(&log);
+#ifdef COMPILER1
+      Runtime1::print_counters_on(&log);
+#endif
+#ifdef COMPILER2
+      OptoRuntime::print_counters_on(&log);
+      Deoptimization::print_counters_on(&log);
+#endif
+    } else {
+      log.print_cr(": no info (%s is disabled)", (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData"));
+    }
+    log.cr();
+    perf_jvm_print_on(&log);
+    log.cr();
+    MethodHandles::print_counters_on(&log);
+  }
+}
+
+void print_bytecode_count() {
+  if (CountBytecodes || TraceBytecodes || StopInterpreterAt) {
+    tty->print_cr("[BytecodeCounter::counter_value = " JLONG_FORMAT "]", BytecodeCounter::counter_value());
+  }
+}
+
 #ifndef PRODUCT
 
 // Statistics printing (method invocation histogram)
 
 GrowableArray<Method*>* collected_invoked_methods;
@@ -224,37 +316,35 @@
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- final",       final_total,   100.0 * (double)final_total  / total_div);
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- static",      static_total,  100.0 * (double)static_total / total_div);
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- native",      native_total,  100.0 * (double)native_total / total_div);
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- accessor",    access_total,  100.0 * (double)access_total / total_div);
   tty->cr();
-  SharedRuntime::print_call_statistics(comp_total);
-}
-
-static void print_bytecode_count() {
-  if (CountBytecodes || TraceBytecodes || StopInterpreterAt) {
-    tty->print_cr("[BytecodeCounter::counter_value = %d]", BytecodeCounter::counter_value());
-  }
+  SharedRuntime::print_call_statistics_on(tty);
 }
 
 #else
 
 static void print_method_invocation_histogram() {}
-static void print_bytecode_count() {}
 
 #endif // PRODUCT
 
 
 // General statistics printing (profiling ...)
 void print_statistics() {
+#if INCLUDE_CDS
+  if (ReplayTraining && PrintTrainingInfo) {
+    TrainingData::print_archived_training_data_on(tty);
+  }
+#endif
   if (CITime) {
     CompileBroker::print_times();
   }
 
 #ifdef COMPILER1
   if ((PrintC1Statistics || LogVMOutput || LogCompilation) && UseCompiler) {
     FlagSetting fs(DisplayVMOutput, DisplayVMOutput && PrintC1Statistics);
-    Runtime1::print_statistics();
+    Runtime1::print_statistics_on(tty);
     SharedRuntime::print_statistics();
   }
 #endif /* COMPILER1 */
 
 #ifdef COMPILER2
@@ -317,10 +407,16 @@
   // CodeHeap State Analytics.
   if (PrintCodeHeapAnalytics) {
     CompileBroker::print_heapinfo(nullptr, "all", 4096); // details
   }
 
+  LogStreamHandle(Debug, codecache, nmethod) log;
+  if (log.is_enabled()) {
+    MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+    CodeCache::print_nmethods_on(&log);
+  }
+
 #ifndef PRODUCT
   if (PrintCodeCache2) {
     MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
     CodeCache::print_internals();
   }
@@ -357,10 +453,12 @@
     CompilationMemoryStatistic::print_all_by_size(tty, false, 0);
   }
 
   ThreadsSMRSupport::log_statistics();
 
+  log_vm_init_stats();
+
   if (log_is_enabled(Info, perf, class, link)) {
     LogStreamHandle(Info, perf, class, link) log;
     log.print_cr("At VM exit:");
     ClassLoader::print_counters(&log);
   }
@@ -422,29 +520,38 @@
     static_cast<void>(LSAN_DO_RECOVERABLE_LEAK_CHECK());
   }
 #endif
 
 #if INCLUDE_CDS
-  // Dynamic CDS dumping must happen whilst we can still reliably
-  // run Java code.
-  DynamicArchive::dump_at_exit(thread, ArchiveClassesAtExit);
-  assert(!thread->has_pending_exception(), "must be");
+  MethodProfiler::process_method_hotness();
 #endif
 
-
   // Actual shutdown logic begins here.
 
 #if INCLUDE_JVMCI
   if (EnableJVMCI) {
     JVMCI::shutdown(thread);
   }
 #endif
 
 #if INCLUDE_CDS
   ClassListWriter::write_resolved_constants();
+  ClassListWriter::write_reflection_data();
+  ClassListWriter::write_loader_negative_lookup_cache();
+  // Dynamic CDS dumping must happen whilst we can still reliably
+  // run Java code.
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    // Creating the hotspot.cds.preimage file
+    MetaspaceShared::preload_and_dump(thread);
+  } else {
+    DynamicArchive::dump_at_exit(thread, ArchiveClassesAtExit);
+  }
+  assert(!thread->has_pending_exception(), "must be");
 #endif
 
+  SCCache::close(); // Write final data and close archive
+
   // Hang forever on exit if we're reporting an error.
   if (ShowMessageBoxOnError && VMError::is_error_reported()) {
     os::infinite_sleep();
   }
 
@@ -481,11 +588,11 @@
       ClassLoaderDataGraph::print_on(&ls_trace);
     }
   }
 
   if (PrintBytecodeHistogram) {
-    BytecodeHistogram::print();
+    BytecodeHistogram::print(PrintBytecodeHistogramCutoff);
   }
 
 #ifdef LINUX
   if (DumpPerfMapAtExit) {
     CodeCache::write_perf_map(nullptr, tty);
@@ -506,10 +613,15 @@
 
   // Terminate the signal thread
   // Note: we don't wait until it actually dies.
   os::terminate_signal_thread();
 
+  if (VerifyTrainingData) {
+    EXCEPTION_MARK;
+    CompilationPolicy::replay_training_at_init(true, THREAD); // implies TrainingData::verify()
+  }
+
   print_statistics();
   Universe::heap()->print_tracing_info();
 
   { MutexLocker ml(BeforeExit_lock);
     _before_exit_status = BEFORE_EXIT_DONE;
diff a/src/hotspot/share/runtime/java.hpp b/src/hotspot/share/runtime/java.hpp
--- a/src/hotspot/share/runtime/java.hpp
+++ b/src/hotspot/share/runtime/java.hpp
@@ -57,10 +57,15 @@
 extern void vm_exit_during_initialization(const char* error, const char* message = nullptr);
 extern void vm_shutdown_during_initialization(const char* error, const char* message = nullptr);
 
 extern void vm_exit_during_cds_dumping(const char* error, const char* message = nullptr);
 
+extern void perf_jvm_print_on(outputStream* st);
+extern void perf_deoptimization_print_on(outputStream* st);
+
+extern void log_vm_init_stats();
+
 // This is defined in linkType.cpp due to linking restraints
 extern bool is_vm_statically_linked();
 
 /**
  * With the integration of the changes to handle the version string
diff a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp
--- a/src/hotspot/share/runtime/javaThread.cpp
+++ b/src/hotspot/share/runtime/javaThread.cpp
@@ -495,10 +495,12 @@
   _cont_fastpath_thread_state(1),
   _held_monitor_count(0),
   _jni_monitor_count(0),
   _unlocked_inflated_monitor(nullptr),
 
+  _can_call_java(true),
+
   _preempt_alternate_return(nullptr),
   _preemption_cancelled(false),
   _pending_interrupted_exception(false),
 
   _handshake(this),
@@ -976,11 +978,11 @@
   }
 
   // We need to cache the thread name for logging purposes below as once
   // we have called on_thread_detach this thread must not access any oops.
   char* thread_name = nullptr;
-  if (log_is_enabled(Debug, os, thread, timer)) {
+  if (log_is_enabled(Debug, os, thread, timer) || (CountBytecodesPerThread && log_is_enabled(Info, init))) {
     ResourceMark rm(this);
     thread_name = os::strdup(name());
   }
 
   if (log_is_enabled(Info, os, thread)) {
@@ -1003,10 +1005,15 @@
       }
     }
   }
 #endif // INCLUDE_JVMCI
 
+  if (bc_counter_value() > 0) {
+    log_info(init)("Thread '%s': " JLONG_FORMAT " bytecodes executed (during clinit: " JLONG_FORMAT ")",
+                   thread_name, bc_counter_value(), clinit_bc_counter_value());
+  }
+
   // Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread.
   // We call BarrierSet::barrier_set()->on_thread_detach() here so no touching of oops after this point.
   Threads::remove(this, daemon);
 
   if (log_is_enabled(Debug, os, thread, timer)) {
@@ -1761,30 +1768,35 @@
 // This is the external format, selecting the platform or vthread
 // as applicable, and allowing for a native-only stack.
 void JavaThread::print_jni_stack() {
   assert(this == JavaThread::current(), "Can't print stack of other threads");
   if (!has_last_Java_frame()) {
-    ResourceMark rm(this);
-    char* buf = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN);
-    if (buf == nullptr) {
-      tty->print_cr("Unable to print native stack - out of memory");
-      return;
-    }
-    address lastpc = nullptr;
-    if (os::platform_print_native_stack(tty, nullptr, buf, O_BUFLEN, lastpc)) {
-      // We have printed the native stack in platform-specific code,
-      // so nothing else to do in this case.
-    } else {
-      frame f = os::current_frame();
-      VMError::print_native_stack(tty, f, this, true /*print_source_info */,
-                                  -1 /* max stack */, buf, O_BUFLEN);
-    }
+    print_native_stack_on(tty);
   } else {
     print_active_stack_on(tty);
   }
 }
 
+void JavaThread::print_native_stack_on(outputStream *st) {
+  assert(this == JavaThread::current(), "Can't print stack of other threads");
+  ResourceMark rm;
+  char* buf = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN);
+  if (buf == nullptr) {
+    st->print_cr("Unable to print native stack - out of memory");
+    return;
+  }
+  address lastpc = nullptr;
+  if (os::platform_print_native_stack(st, nullptr, buf, O_BUFLEN, lastpc)) {
+    // We have printed the native stack in platform-specific code,
+    // so nothing else to do in this case.
+  } else {
+    frame f = os::current_frame();
+    VMError::print_native_stack(st, f, this, true /*print_source_info */,
+                                -1 /* max stack */, buf, O_BUFLEN);
+  }
+}
+
 void JavaThread::print_stack_on(outputStream* st) {
   if (!has_last_Java_frame()) return;
 
   Thread* current_thread = Thread::current();
   ResourceMark rm(current_thread);
@@ -2359,6 +2371,6 @@
     event->set_blockingOperation(op);
     event->set_carrierThread(JFR_JVM_THREAD_ID(this));
     event->commit();
   }
 }
-#endif
+#endif
diff a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp
--- a/src/hotspot/share/runtime/javaThread.hpp
+++ b/src/hotspot/share/runtime/javaThread.hpp
@@ -478,10 +478,12 @@
   // It's signed for error detection.
   intx _held_monitor_count;  // used by continuations for fast lock detection
   intx _jni_monitor_count;
   ObjectMonitor* _unlocked_inflated_monitor;
 
+  bool _can_call_java;
+
   // This is the field we poke in the interpreter and native
   // wrapper (Object.wait) to check for preemption.
   address _preempt_alternate_return;
   // When preempting on monitorenter we could have acquired the
   // monitor after freezing all vthread frames. In that case we
@@ -552,13 +554,26 @@
   };
   void exit(bool destroy_vm, ExitType exit_type = normal_exit);
 
   void cleanup_failed_attach_current_thread(bool is_daemon);
 
+  class NoJavaCodeMark : public StackObj {
+    friend JavaThread;
+    JavaThread* _target;
+    bool _orig;
+   public:
+    NoJavaCodeMark(JavaThread* t) : _target(t), _orig(t->_can_call_java) {
+      _target->_can_call_java = false;
+    }
+    ~NoJavaCodeMark() {
+      _target->_can_call_java = _orig;
+    }
+  };
+
   // Testers
   virtual bool is_Java_thread() const            { return true;  }
-  virtual bool can_call_java() const             { return true; }
+  virtual bool can_call_java() const             { return _can_call_java; }
 
   virtual bool is_active_Java_thread() const;
 
   // Thread oop. threadObj() can be null for initial JavaThread
   // (or for threads attached via JNI)
@@ -1027,10 +1042,12 @@
   // Print current stack trace for checked JNI warnings and JNI fatal errors.
   // This is the external format from above, but selecting the platform
   // or vthread as applicable.
   void print_jni_stack();
 
+  void print_native_stack_on(outputStream* st);
+
   // Print stack traces in various internal formats
   void trace_stack()                             PRODUCT_RETURN;
   void trace_stack_from(vframe* start_vf)        PRODUCT_RETURN;
   void trace_frames()                            PRODUCT_RETURN;
 
@@ -1209,12 +1226,13 @@
   // Track the class we want to initialize but for which we have to wait
   // on its init_lock() because it is already being initialized.
   void set_class_to_be_initialized(InstanceKlass* k);
   InstanceKlass* class_to_be_initialized() const;
 
-  // Track executing class initializer, see ThreadInClassInitializer
-  void set_class_being_initialized(InstanceKlass* k);
+  // The most recent active <clinit> invocation is tracked by this variable.
+  // The setter returns the previous value, so it can be restored later if needed.
+  InstanceKlass* set_class_being_initialized(InstanceKlass* k);
   InstanceKlass* class_being_initialized() const;
 
 private:
   InstanceKlass* _class_to_be_initialized;
   InstanceKlass* _class_being_initialized;
diff a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp
--- a/src/hotspot/share/runtime/javaThread.inline.hpp
+++ b/src/hotspot/share/runtime/javaThread.inline.hpp
@@ -239,17 +239,19 @@
 
 inline InstanceKlass* JavaThread::class_to_be_initialized() const {
   return _class_to_be_initialized;
 }
 
-inline void JavaThread::set_class_being_initialized(InstanceKlass* k) {
-  assert(k != nullptr || _class_being_initialized != nullptr, "incorrect usage");
+inline InstanceKlass* JavaThread::set_class_being_initialized(InstanceKlass* k) {
   assert(this == Thread::current(), "Only the current thread can set this field");
+  InstanceKlass* prev = _class_being_initialized;
   _class_being_initialized = k;
+  return prev;
 }
 
 inline InstanceKlass* JavaThread::class_being_initialized() const {
+  assert(this == Thread::current(), "Only the current thread can get this field");
   return _class_being_initialized;
 }
 
 inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) {
   assert(UseObjectMonitorTable, "must be");
diff a/src/hotspot/share/runtime/methodDetails.cpp b/src/hotspot/share/runtime/methodDetails.cpp
--- /dev/null
+++ b/src/hotspot/share/runtime/methodDetails.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1997, 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+
+#include "compiler/abstractCompiler.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/disassembler.hpp"
+#include "oops/oop.inline.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/methodDetails.hpp"
+
+Symbol* MethodDetails::class_name() {
+  if (_class_name == nullptr) {
+    if (_method_handle != nullptr) {
+      _class_name = (*_method_handle)->method_holder()->name();
+    } else if (_ci_method != nullptr) {
+      _class_name = _ci_method->holder()->name()->get_symbol();
+    } else if (_method != nullptr) {
+      _class_name = _method->method_holder()->name();
+    }
+  }
+  return _class_name;
+}
+
+Symbol* MethodDetails::method_name() {
+  if (_method_name == nullptr) {
+    if (_method_handle != nullptr) {
+      _method_name = (*_method_handle)->name();
+    } else if (_ci_method != nullptr) {
+      _method_name = _ci_method->name()->get_symbol();
+    } else if (_method != nullptr) {
+      _method_name = _method->name();
+    }
+  }
+  return _method_name;
+}
+
+Symbol* MethodDetails::signature() {
+  if (_signature == nullptr) {
+    if (_method_handle != nullptr) {
+      _signature = (*_method_handle)->signature();
+    } else if (_ci_method != nullptr) {
+      _signature = _ci_method->signature()->as_symbol()->get_symbol();
+    } else if (_method != nullptr) {
+      _signature = _method->signature();
+    }
+  }
+  return _signature;
+}
diff a/src/hotspot/share/runtime/methodDetails.hpp b/src/hotspot/share/runtime/methodDetails.hpp
--- /dev/null
+++ b/src/hotspot/share/runtime/methodDetails.hpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1997, 2024, 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.
+ *
+ */
+
+#ifndef SHARE_RUNTIME_METHOD_DETAILS_HPP
+#define SHARE_RUNTIME_METHOD_DETAILS_HPP
+
+#include "code/codeBlob.hpp"
+#include "code/vmreg.hpp"
+#include "interpreter/linkResolver.hpp"
+#include "memory/allStatic.hpp"
+#include "memory/resourceArea.hpp"
+#include "utilities/macros.hpp"
+
+class ciMethod;
+
+class MethodDetails: public CHeapObj<mtInternal> {
+private:
+  const methodHandle* _method_handle;
+  const ciMethod* _ci_method;
+  const Method* _method;
+
+  Symbol* _class_name;
+  Symbol* _method_name;
+  Symbol* _signature;
+
+  MethodDetails(const methodHandle* method_handle, const ciMethod* ci_method, const Method* method) :
+    _method_handle(method_handle), _ci_method(ci_method), _method(method),
+    _class_name(nullptr), _method_name(nullptr), _signature(nullptr)
+    {};
+
+public:
+  MethodDetails(const methodHandle& method) :
+    MethodDetails(&method, nullptr, nullptr) {}
+
+  MethodDetails(const methodHandle* method) :
+    MethodDetails(method, nullptr, nullptr) {}
+
+  MethodDetails(const ciMethod* method) :
+    MethodDetails(nullptr, method, nullptr) {}
+
+  MethodDetails(const Method* method) :
+    MethodDetails(nullptr, nullptr, method) {}
+
+  Symbol* class_name();
+  Symbol* method_name();
+  Symbol* signature();
+};
+
+#endif // SHARE_RUNTIME_METHOD_DETAILS_HPP
diff a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp
--- a/src/hotspot/share/runtime/mutex.cpp
+++ b/src/hotspot/share/runtime/mutex.cpp
@@ -270,14 +270,15 @@
 Mutex::~Mutex() {
   assert_owner(nullptr);
   os::free(const_cast<char*>(_name));
 }
 
-Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(nullptr) {
+Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(nullptr), _id(-1) {
   assert(os::mutex_init_done(), "Too early!");
   assert(name != nullptr, "Mutex requires a name");
   _name = os::strdup(name, mtInternal);
+  _id = MutexLocker::name2id(name);
 #ifdef ASSERT
   _allow_vm_block  = allow_vm_block;
   _rank            = rank;
   _skip_rank_check = false;
 
diff a/src/hotspot/share/runtime/mutex.hpp b/src/hotspot/share/runtime/mutex.hpp
--- a/src/hotspot/share/runtime/mutex.hpp
+++ b/src/hotspot/share/runtime/mutex.hpp
@@ -96,10 +96,11 @@
   void raw_set_owner(Thread* new_owner) { Atomic::store(&_owner, new_owner); }
 
  protected:                              // Monitor-Mutex metadata
   PlatformMonitor _lock;                 // Native monitor implementation
   const char* _name;                     // Name of mutex/monitor
+  int _id;                               // ID for named mutexes
 
   // Debugging fields for naming, deadlock detection, etc. (some only used in debug mode)
 #ifndef PRODUCT
   bool    _allow_vm_block;
 #endif
@@ -192,10 +193,15 @@
   void set_owner(Thread* owner) { set_owner_implementation(owner); }
   bool owned_by_self() const;
 
   const char *name() const                  { return _name; }
 
+  int      id() const { return _id; }
+//  void set_id(int id) { _id = id; }
+
+  static const char* id2name(int id);
+
   void print_on_error(outputStream* st) const;
   #ifndef PRODUCT
     void print_on(outputStream* st) const;
     void print() const;
   #endif
diff a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -21,19 +21,22 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "compiler/compiler_globals.hpp"
 #include "gc/shared/gc_globals.hpp"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
+#include "runtime/java.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/vmThread.hpp"
+#include "services/management.hpp"
 #include "utilities/vmError.hpp"
 
 // Mutexes used in the VM (see comment in mutexLocker.hpp):
 
 Mutex*   NMethodState_lock            = nullptr;
@@ -78,20 +81,27 @@
 Mutex*   MarkStackChunkList_lock      = nullptr;
 Mutex*   MonitoringSupport_lock       = nullptr;
 Monitor* ConcurrentGCBreakpoints_lock = nullptr;
 Mutex*   Compile_lock                 = nullptr;
 Monitor* MethodCompileQueue_lock      = nullptr;
+Monitor* MethodCompileQueueC1_lock    = nullptr;
+Monitor* MethodCompileQueueC2_lock    = nullptr;
+Monitor* MethodCompileQueueC3_lock    = nullptr;
+Monitor* MethodCompileQueueSC1_lock   = nullptr;
+Monitor* MethodCompileQueueSC2_lock   = nullptr;
 Monitor* CompileThread_lock           = nullptr;
 Monitor* Compilation_lock             = nullptr;
 Mutex*   CompileTaskAlloc_lock        = nullptr;
 Mutex*   CompileStatistics_lock       = nullptr;
 Mutex*   DirectivesStack_lock         = nullptr;
 Monitor* Terminator_lock              = nullptr;
 Monitor* InitCompleted_lock           = nullptr;
 Monitor* BeforeExit_lock              = nullptr;
 Monitor* Notify_lock                  = nullptr;
 Mutex*   ExceptionCache_lock          = nullptr;
+Mutex*   TrainingData_lock            = nullptr;
+Monitor* TrainingReplayQueue_lock     = nullptr;
 #ifndef PRODUCT
 Mutex*   FullGCALot_lock              = nullptr;
 #endif
 
 Mutex*   tty_lock                     = nullptr;
@@ -145,10 +155,11 @@
 Mutex*   DumpRegion_lock              = nullptr;
 Mutex*   ClassListFile_lock           = nullptr;
 Mutex*   UnregisteredClassesTable_lock= nullptr;
 Mutex*   LambdaFormInvokers_lock      = nullptr;
 Mutex*   ScratchObjects_lock          = nullptr;
+Mutex*   ArchivedObjectTables_lock    = nullptr;
 #endif // INCLUDE_CDS
 Mutex*   Bootclasspath_lock           = nullptr;
 
 #if INCLUDE_JVMCI
 Monitor* JVMCI_lock                   = nullptr;
@@ -182,11 +193,13 @@
 }
 #endif
 
 static void add_mutex(Mutex* var) {
   assert(_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX");
-  _mutex_array[_num_mutex++] = var;
+  int id = _num_mutex++;
+  _mutex_array[id] = var;
+//  var->set_id(id);
 }
 
 #define MUTEX_STORAGE_NAME(name) name##_storage
 #define MUTEX_STORAGE(name, type) alignas(type) static uint8_t MUTEX_STORAGE_NAME(name)[sizeof(type)]
 #define MUTEX_DEF(name, type, pri, ...) {                                                       \
@@ -260,10 +273,25 @@
 
   MUTEX_DEFN(JfieldIdCreation_lock           , PaddedMutex  , safepoint);
 
   MUTEX_DEFN(CompiledIC_lock                 , PaddedMutex  , nosafepoint);  // locks VtableStubs_lock
   MUTEX_DEFN(MethodCompileQueue_lock         , PaddedMonitor, safepoint);
+  if (UseGlobalCompileQueueLock) {
+    MethodCompileQueueC1_lock  = MethodCompileQueue_lock;
+    MethodCompileQueueC2_lock  = MethodCompileQueue_lock;
+    MethodCompileQueueC3_lock  = MethodCompileQueue_lock;
+    MethodCompileQueueSC1_lock = MethodCompileQueue_lock;
+    MethodCompileQueueSC2_lock = MethodCompileQueue_lock;
+  } else {
+    MUTEX_DEFN(MethodCompileQueueC1_lock     , PaddedMonitor, safepoint);
+    MUTEX_DEFN(MethodCompileQueueC2_lock     , PaddedMonitor, safepoint);
+    MUTEX_DEFN(MethodCompileQueueC3_lock     , PaddedMonitor, safepoint);
+    MUTEX_DEFN(MethodCompileQueueSC1_lock    , PaddedMonitor, safepoint);
+    MUTEX_DEFN(MethodCompileQueueSC2_lock    , PaddedMonitor, safepoint);
+  }
+  MUTEX_DEFL(TrainingData_lock               , PaddedMutex  , MethodCompileQueue_lock);
+  MUTEX_DEFN(TrainingReplayQueue_lock        , PaddedMonitor, safepoint);
   MUTEX_DEFN(CompileStatistics_lock          , PaddedMutex  , safepoint);
   MUTEX_DEFN(DirectivesStack_lock            , PaddedMutex  , nosafepoint);
 
   MUTEX_DEFN(JvmtiVTMSTransition_lock        , PaddedMonitor, safepoint);   // used for Virtual Thread Mount State transition management
   MUTEX_DEFN(EscapeBarrier_lock              , PaddedMonitor, nosafepoint); // Used to synchronize object reallocation/relocking triggered by JVMTI
@@ -276,13 +304,11 @@
   MUTEX_DEFN(PeriodicTask_lock               , PaddedMonitor, safepoint, true);
   MUTEX_DEFN(RedefineClasses_lock            , PaddedMonitor, safepoint);
   MUTEX_DEFN(Verify_lock                     , PaddedMutex  , safepoint);
   MUTEX_DEFN(ClassLoaderDataGraph_lock       , PaddedMutex  , safepoint);
 
-  if (WhiteBoxAPI) {
-    MUTEX_DEFN(Compilation_lock              , PaddedMonitor, nosafepoint);
-  }
+  MUTEX_DEFN(Compilation_lock                , PaddedMonitor, nosafepoint);
 
 #if INCLUDE_JFR
   MUTEX_DEFN(JfrBuffer_lock                  , PaddedMutex  , event);
   MUTEX_DEFN(JfrMsg_lock                     , PaddedMonitor, event);
   MUTEX_DEFN(JfrStacktrace_lock              , PaddedMutex  , event);
@@ -306,10 +332,11 @@
   MUTEX_DEFN(DumpRegion_lock                 , PaddedMutex  , nosafepoint);
   MUTEX_DEFN(ClassListFile_lock              , PaddedMutex  , nosafepoint);
   MUTEX_DEFN(UnregisteredClassesTable_lock   , PaddedMutex  , nosafepoint-1);
   MUTEX_DEFN(LambdaFormInvokers_lock         , PaddedMutex  , safepoint);
   MUTEX_DEFN(ScratchObjects_lock             , PaddedMutex  , nosafepoint-1); // Holds DumpTimeTable_lock
+  MUTEX_DEFN(ArchivedObjectTables_lock       , PaddedMutex  , nosafepoint);
 #endif // INCLUDE_CDS
   MUTEX_DEFN(Bootclasspath_lock              , PaddedMutex  , nosafepoint);
 
 #if INCLUDE_JVMCI
   // JVMCIRuntime::_lock must be acquired before JVMCI_lock to avoid deadlock
@@ -326,11 +353,11 @@
   // tty_lock is held when printing nmethod and its relocations which use this lock.
   MUTEX_DEFL(ExternalsRecorder_lock         , PaddedMutex  , tty_lock);
 
   MUTEX_DEFL(Threads_lock                   , PaddedMonitor, CompileThread_lock, true);
   MUTEX_DEFL(Compile_lock                   , PaddedMutex  , MethodCompileQueue_lock);
-  MUTEX_DEFL(Heap_lock                      , PaddedMonitor, AdapterHandlerLibrary_lock);
+  MUTEX_DEFL(Heap_lock                      , PaddedMonitor, TrainingData_lock  /*AdapterHandlerLibrary_lock*/);
 
   MUTEX_DEFL(PerfDataMemAlloc_lock          , PaddedMutex  , Heap_lock);
   MUTEX_DEFL(PerfDataManager_lock           , PaddedMutex  , Heap_lock);
   MUTEX_DEFL(VMOperation_lock               , PaddedMonitor, Heap_lock, true);
   MUTEX_DEFL(ClassInitError_lock            , PaddedMonitor, Threads_lock);
@@ -363,10 +390,109 @@
 #undef MUTEX_DEFN
 #undef MUTEX_DEF
 #undef MUTEX_STORAGE
 #undef MUTEX_STORAGE_NAME
 
+static const int MAX_NAMES = 200;
+static const char* _names[MAX_NAMES] = { nullptr };
+static bool _is_unique[MAX_NAMES] = { false };
+static int _num_names = 0;
+
+PerfCounter** MutexLockerImpl::_perf_lock_count     = nullptr;
+PerfCounter** MutexLockerImpl::_perf_lock_wait_time = nullptr;
+PerfCounter** MutexLockerImpl::_perf_lock_hold_time = nullptr;
+
+void MutexLockerImpl::init_counters() {
+  if (ProfileVMLocks && UsePerfData) {
+    ResourceMark rm;
+    EXCEPTION_MARK;
+    _perf_lock_count     = NEW_C_HEAP_ARRAY(PerfCounter*, MAX_NAMES + 1, mtInternal);
+    _perf_lock_wait_time = NEW_C_HEAP_ARRAY(PerfCounter*, MAX_NAMES + 1, mtInternal);
+    _perf_lock_hold_time = NEW_C_HEAP_ARRAY(PerfCounter*, MAX_NAMES + 1, mtInternal);
+
+    NEWPERFEVENTCOUNTER(_perf_lock_count[0],     SUN_RT, PerfDataManager::counter_name("Other", "Count"));
+    NEWPERFEVENTCOUNTER(_perf_lock_wait_time[0], SUN_RT, PerfDataManager::counter_name("Other", "BeforeTime"));
+    NEWPERFEVENTCOUNTER(_perf_lock_hold_time[0], SUN_RT, PerfDataManager::counter_name("Other", "AfterTime"));
+    for (int i = 0; i < MAX_NAMES; i++) {
+      ResourceMark rm;
+      const char* counter_name = _names[i];
+      if (counter_name == nullptr) {
+        stringStream ss;
+        ss.print("UnnamedMutex#%d", i);
+        counter_name = ss.as_string();
+      }
+      NEWPERFEVENTCOUNTER(_perf_lock_count[i + 1],     SUN_RT, PerfDataManager::counter_name(counter_name, "Count"));
+      NEWPERFEVENTCOUNTER(_perf_lock_wait_time[i + 1], SUN_RT, PerfDataManager::counter_name(counter_name, "BeforeTime"));
+      NEWPERFEVENTCOUNTER(_perf_lock_hold_time[i + 1], SUN_RT, PerfDataManager::counter_name(counter_name, "AfterTime"));
+    }
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("MutexLockerImpl::init_counters() failed unexpectedly");
+    }
+  }
+}
+
+int MutexLockerImpl::name2id(const char* name) {
+  if (ProfileVMLocks && UsePerfData) {
+    for (int i = 0; i < _num_names; i++) {
+      if (strcmp(_names[i], name) == 0) {
+        _is_unique[i] = false;
+        return i;
+      }
+    }
+    if (_num_names < MAX_NAMES) {
+      int new_id = _num_names++;
+      _names[new_id] = os::strdup(name, mtInternal);
+      _is_unique[new_id] = true;
+      return new_id;
+    }
+    log_debug(init)("Unnamed: %s", name); // no slots left
+  }
+  return -1;
+}
+
+void MutexLockerImpl::print_counter_on(outputStream* st, const char* name, bool is_unique, int idx) {
+  jlong count = _perf_lock_count[idx]->get_value();
+  if (count > 0) {
+    st->print_cr("  %3d: %s%40s = " JLONG_FORMAT_W(5) "us (" JLONG_FORMAT_W(5) "us) / " JLONG_FORMAT_W(9) " events",
+                 idx, (is_unique ? " " : "M"), name,
+                 Management::ticks_to_us(_perf_lock_hold_time[idx]->get_value()),
+                 Management::ticks_to_us(_perf_lock_wait_time[idx]->get_value()),
+                 count);
+  }
+}
+
+static jlong accumulate_lock_counters(PerfCounter** lock_counters) {
+  jlong acc = 0;
+  for (int i = 0; i < _num_names + 1; i++) { // 0 slot is reserved for unnamed locks
+    if (lock_counters[i] == nullptr) {
+      break;
+    }
+    acc += lock_counters[i]->get_value();
+  }
+  return acc;
+}
+
+void MutexLockerImpl::print_counters_on(outputStream* st) {
+  if (ProfileVMLocks && UsePerfData) {
+    jlong total_count     = accumulate_lock_counters(_perf_lock_count);
+    jlong total_wait_time = accumulate_lock_counters(_perf_lock_wait_time);
+    jlong total_hold_time = accumulate_lock_counters(_perf_lock_hold_time);
+
+    st->print_cr("MutexLocker: Total: %d named locks (%d unique names); hold = " JLONG_FORMAT "us (wait = " JLONG_FORMAT "us) / " JLONG_FORMAT " events for thread \"main\"",
+                 _num_mutex, _num_names,
+                 Management::ticks_to_us(total_hold_time),
+                 Management::ticks_to_us(total_wait_time),
+                 total_count);
+    for (int i = 0; i < _num_names; i++) {
+      print_counter_on(st, _names[i], _is_unique[i], i+1);
+    }
+    print_counter_on(st, "Unnamed / Other", false /*is_unique*/, 0);
+  } else {
+    st->print_cr("MutexLocker: no info (%s is disabled)", (UsePerfData ? "ProfileVMLocks" : "UsePerfData"));
+  }
+}
+
 void MutexLockerImpl::post_initialize() {
   // Print mutex ranks if requested.
   LogTarget(Info, vmmutex) lt;
   if (lt.is_enabled()) {
     ResourceMark rm;
diff a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
--- a/src/hotspot/share/runtime/mutexLocker.hpp
+++ b/src/hotspot/share/runtime/mutexLocker.hpp
@@ -23,13 +23,16 @@
  */
 
 #ifndef SHARE_RUNTIME_MUTEXLOCKER_HPP
 #define SHARE_RUNTIME_MUTEXLOCKER_HPP
 
+#include "logging/log.hpp"
 #include "memory/allocation.hpp"
 #include "runtime/flags/flagSetting.hpp"
 #include "runtime/mutex.hpp"
+#include "runtime/perfData.hpp"
+#include "runtime/thread.hpp"
 
 class Thread;
 
 // Mutexes used in the VM.
 
@@ -78,12 +81,19 @@
 extern Mutex*   MarkStackChunkList_lock;         // Protects access to the global mark stack chunk list.
 extern Mutex*   MonitoringSupport_lock;          // Protects updates to the serviceability memory pools and allocated memory high water mark.
 extern Monitor* ConcurrentGCBreakpoints_lock;    // Protects concurrent GC breakpoint management
 extern Mutex*   Compile_lock;                    // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)
 extern Monitor* MethodCompileQueue_lock;         // a lock held when method compilations are enqueued, dequeued
+extern Monitor* MethodCompileQueueC1_lock;       // a lock held when method compilations are enqueued, dequeued
+extern Monitor* MethodCompileQueueC2_lock;       // a lock held when method compilations are enqueued, dequeued
+extern Monitor* MethodCompileQueueC3_lock;       // a lock held when method compilations are enqueued, dequeued
+extern Monitor* MethodCompileQueueSC1_lock;      // a lock held when method compilations are enqueued, dequeued
+extern Monitor* MethodCompileQueueSC2_lock;      // a lock held when method compilations are enqueued, dequeued
 extern Monitor* CompileThread_lock;              // a lock held by compile threads during compilation system initialization
 extern Monitor* Compilation_lock;                // a lock used to pause compilation
+extern Mutex*   TrainingData_lock;               // a lock used when accessing training records
+extern Monitor* TrainingReplayQueue_lock;        // a lock held when class are added/removed to the training replay queue
 extern Mutex*   CompileTaskAlloc_lock;           // a lock held when CompileTasks are allocated
 extern Mutex*   CompileStatistics_lock;          // a lock held when updating compilation statistics
 extern Mutex*   DirectivesStack_lock;            // a lock held when mutating the dirstack and ref counting directives
 extern Monitor* Terminator_lock;                 // a lock used to guard termination of the vm
 extern Monitor* InitCompleted_lock;              // a lock used to signal threads waiting on init completed
@@ -126,10 +136,11 @@
 extern Mutex*   DumpRegion_lock;                 // Symbol::operator new(size_t sz, int len)
 extern Mutex*   ClassListFile_lock;              // ClassListWriter()
 extern Mutex*   UnregisteredClassesTable_lock;   // UnregisteredClassesTableTable
 extern Mutex*   LambdaFormInvokers_lock;         // Protecting LambdaFormInvokers::_lambdaform_lines
 extern Mutex*   ScratchObjects_lock;             // Protecting _scratch_xxx_table in heapShared.cpp
+extern Mutex*   ArchivedObjectTables_lock;       // Protecting the table used by HeapShared::get_archived_object_permanent_index()
 #endif // INCLUDE_CDS
 #if INCLUDE_JFR
 extern Mutex*   JfrStacktrace_lock;              // used to guard access to the JFR stacktrace table
 extern Monitor* JfrMsg_lock;                     // protects JFR messaging
 extern Mutex*   JfrBuffer_lock;                  // protects JFR buffer operations
@@ -188,44 +199,83 @@
 // Internal implementation. Skips on null Mutex.
 // Subclasses enforce stronger invariants.
 class MutexLockerImpl: public StackObj {
  protected:
   Mutex* _mutex;
+  bool _prof;
+  elapsedTimer _before;
+  elapsedTimer _after;
+
+private:
+  static PerfCounter** _perf_lock_count;
+  static PerfCounter** _perf_lock_wait_time;
+  static PerfCounter** _perf_lock_hold_time;
+
+public:
 
   MutexLockerImpl(Mutex* mutex, Mutex::SafepointCheckFlag flag = Mutex::_safepoint_check_flag) :
-    _mutex(mutex) {
+    _mutex(mutex), _prof(ProfileVMLocks && Thread::current_or_null() != nullptr && Thread::current()->profile_vm_locks()) {
+
     bool no_safepoint_check = flag == Mutex::_no_safepoint_check_flag;
     if (_mutex != nullptr) {
+      if (_prof) { _before.start(); } // before
+
       if (no_safepoint_check) {
         _mutex->lock_without_safepoint_check();
       } else {
         _mutex->lock();
       }
+
+      if (_prof) { _before.stop(); _after.start(); } // after
     }
   }
 
   MutexLockerImpl(Thread* thread, Mutex* mutex, Mutex::SafepointCheckFlag flag = Mutex::_safepoint_check_flag) :
-    _mutex(mutex) {
+    _mutex(mutex), _prof(thread->profile_vm_locks()) {
+
+    if (_prof) { _before.start(); } // before
+
     bool no_safepoint_check = flag == Mutex::_no_safepoint_check_flag;
     if (_mutex != nullptr) {
       if (no_safepoint_check) {
         _mutex->lock_without_safepoint_check(thread);
       } else {
         _mutex->lock(thread);
       }
     }
+
+    if (_prof) { _before.stop(); _after.start(); } // after
   }
 
   ~MutexLockerImpl() {
     if (_mutex != nullptr) {
       assert_lock_strong(_mutex);
       _mutex->unlock();
+
+      if (_mutex->id() == -1) {
+        log_trace(init)("Unnamed unclassified lock: %s", _mutex->name());
+      }
+
+      if (_prof) {
+        assert(UsePerfData, "required");
+        _after.stop();
+        _perf_lock_count    [_mutex->id() + 1]->inc();
+        _perf_lock_wait_time[_mutex->id() + 1]->inc(_before.ticks());
+        _perf_lock_hold_time[_mutex->id() + 1]->inc(_after.ticks());
+      }
     }
   }
 
+ private:
+  static void print_counter_on(outputStream* st, const char* name, bool is_unique, int idx);
+
  public:
+  static int name2id(const char* name);
+
   static void post_initialize();
+  static void init_counters();
+  static void print_counters_on(outputStream* st);
 };
 
 // Simplest mutex locker.
 // Does not allow null mutexes.
 class MutexLocker: public MutexLockerImpl {
diff a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp
--- a/src/hotspot/share/runtime/perfData.cpp
+++ b/src/hotspot/share/runtime/perfData.cpp
@@ -525,5 +525,12 @@
   assert(copy != nullptr, "just checking");
 
   return copy;
 }
 
+PerfTraceTimeBase::~PerfTraceTimeBase() {
+  if (!UsePerfData || !_t->is_active()) return;
+  if (_counter != nullptr) {
+    _t->stop();
+    _counter->inc(_t->ticks());
+  }
+}
diff a/src/hotspot/share/runtime/perfData.hpp b/src/hotspot/share/runtime/perfData.hpp
--- a/src/hotspot/share/runtime/perfData.hpp
+++ b/src/hotspot/share/runtime/perfData.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2024, 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.
@@ -415,10 +415,11 @@
 
   public:
     inline void inc() { (*(jlong*)_valuep)++; }
     inline void inc(jlong val) { (*(jlong*)_valuep) += val; }
     inline void dec(jlong val) { inc(-val); }
+    inline void reset() { (*(jlong*)_valuep) = 0; }
 };
 
 /*
  * The PerfLongCounter class, and its alias PerfCounter, implement
  * a PerfData subtype that holds a jlong data value that can (should)
@@ -621,10 +622,45 @@
     // some form of iterator to provide a cleaner abstraction for
     // iteration over the container.
     inline PerfData* at(int index);
 };
 
+class PerfTickCounters : public CHeapObj<mtInternal> {
+private:
+  const char* _name;
+  PerfCounter* const _elapsed_counter;
+  PerfCounter* const _thread_counter;
+public:
+  PerfTickCounters(const char* name, PerfCounter* elapsed_counter, PerfCounter* thread_counter) :
+                   _name(name), _elapsed_counter(elapsed_counter), _thread_counter(thread_counter) {
+  }
+
+  const char* name() { return _name; }
+
+  PerfCounter* elapsed_counter() const {
+    return _elapsed_counter;
+  }
+  long elapsed_counter_value() const {
+    return _elapsed_counter->get_value();
+  }
+  inline jlong elapsed_counter_value_ms() const;
+  inline jlong elapsed_counter_value_us() const;
+
+  PerfCounter* thread_counter() const {
+    return _thread_counter;
+  }
+  jlong thread_counter_value() const {
+    return _thread_counter->get_value();
+  }
+  inline jlong thread_counter_value_ms() const;
+  inline jlong thread_counter_value_us() const;
+
+  void reset() {
+    _elapsed_counter->reset();
+    _thread_counter->reset();
+  }
+};
 
 /*
  * The PerfDataManager class is responsible for creating PerfData
  * subtypes via a set a factory methods and for managing lists
  * of the various PerfData types.
@@ -785,10 +821,22 @@
                                        PerfData::Units u,
                                        PerfSampleHelper* sh, TRAPS) {
       return create_long_counter(ns, name, u, sh, THREAD);
     }
 
+    static PerfTickCounters* create_tick_counters(CounterNS ns,
+                                                  const char* counter_name,
+                                                  const char* elapsed_counter_name,
+                                                  const char* thread_counter_name,
+                                                  PerfData::Units u, TRAPS) {
+      PerfCounter* elapsed_counter = create_long_counter(ns, elapsed_counter_name, u, (jlong)0, THREAD);
+      PerfCounter* thread_counter = create_long_counter(ns, thread_counter_name, u, (jlong)0, THREAD);
+
+      PerfTickCounters* counters = new PerfTickCounters(counter_name, elapsed_counter, thread_counter);
+      return counters;
+    }
+
     static void destroy();
     static bool has_PerfData() { return _has_PerfData; }
 };
 
 // Useful macros to create the performance counters
@@ -802,81 +850,184 @@
 
 #define NEWPERFBYTECOUNTER(counter, counter_ns, counter_name)  \
   {counter = PerfDataManager::create_counter(counter_ns, counter_name, \
                                              PerfData::U_Bytes,CHECK);}
 
+#define NEWPERFTICKCOUNTERS(counter, counter_ns, counter_name)  \
+  {counter = PerfDataManager::create_tick_counters(counter_ns, counter_name, counter_name "_elapsed_time", \
+                                             counter_name "_thread_time", PerfData::U_Ticks,CHECK);}
+
 // Utility Classes
 
-/*
- * this class will administer a PerfCounter used as a time accumulator
+/* PerfTraceElapsedTime and PerfTraceThreadTime will administer a PerfCounter used as a time accumulator
  * for a basic block much like the TraceTime class.
+ * PerfTraceElapsedTime uses elapsedTimer to measure time which reflects the elapsed time,
+ * and PerfTraceThreadTime uses ThreadTimer which reflects thread cpu time.
  *
  * Example:
  *
  *    static PerfCounter* my_time_counter = PerfDataManager::create_counter("my.time.counter", PerfData::U_Ticks, 0LL, CHECK);
  *
  *    {
- *      PerfTraceTime ptt(my_time_counter);
+ *      PerfTraceElapsedTime ptt(my_time_counter);
  *      // perform the operation you want to measure
  *    }
  *
  * Note: use of this class does not need to occur within a guarded
  * block. The UsePerfData guard is used with the implementation
  * of this class.
  */
-class PerfTraceTime : public StackObj {
 
+class PerfTraceTimeBase : public StackObj {
+  friend class PerfPauseTimer;
+  private:
+    BaseTimer* _t;
+  protected:
+    PerfLongCounter* _counter;
+
+  public:
+    inline PerfTraceTimeBase(BaseTimer* t, PerfLongCounter* counter) : _t(t), _counter(counter) {}
+
+    ~PerfTraceTimeBase();
+
+    jlong active_ticks() { return _t->active_ticks(); }
+
+    const char* name() { return _counter->name(); }
+    BaseTimer* timer() { return _t; }
+};
+
+class PerfTraceElapsedTime: public PerfTraceTimeBase {
   protected:
     elapsedTimer _t;
-    PerfLongCounter* _timerp;
 
   public:
-    inline PerfTraceTime(PerfLongCounter* timerp) : _timerp(timerp) {
-      if (!UsePerfData || timerp == nullptr) { return; }
+    inline PerfTraceElapsedTime(PerfCounter* counter) : PerfTraceTimeBase(&_t, counter) {
+      if (!UsePerfData || counter == nullptr) { return; }
+      _t.start();
+    }
+};
+
+class PerfTraceThreadTime: public PerfTraceTimeBase {
+  protected:
+    ThreadTimer _t;
+
+  public:
+    inline PerfTraceThreadTime(PerfCounter* counter) : PerfTraceTimeBase(&_t, counter) {
+      if (!UsePerfData || !TraceThreadTime || counter == nullptr) { return; }
       _t.start();
     }
+};
+
+// PerfTraceTime is a utility class to provide the ability to measure both elapsed and thread cpu time using a single object.
+class PerfTraceTime : public StackObj {
+  friend class PerfPauseTimer;
+  private:
+    PerfTickCounters* _counters;
+    PerfTraceElapsedTime _elapsed_timer;
+    PerfTraceThreadTime _thread_timer;
+
+  public:
+    inline PerfTraceTime(PerfTickCounters* counters, bool is_on = true):
+                         _counters(counters),
+                         _elapsed_timer(counters != nullptr ? counters->elapsed_counter() : nullptr),
+                         _thread_timer(counters != nullptr ? counters->thread_counter() : nullptr) {}
+
+    const char* name() { return _counters->name(); }
+    PerfTraceTimeBase* elapsed_timer() { return &_elapsed_timer; }
+    PerfTraceTimeBase* thread_timer() { return &_thread_timer; }
 
-    const char* name() const {
-      assert(_timerp != nullptr, "sanity");
-      return _timerp->name();
+    jlong elapsed_timer_active_ticks() {
+      return _elapsed_timer.active_ticks();
     }
 
-    ~PerfTraceTime() {
-      if (!UsePerfData || !_t.is_active()) { return; }
-      _t.stop();
-      _timerp->inc(_t.ticks());
+    jlong thread_timer_active_ticks() {
+      return _thread_timer.active_ticks();
     }
 };
 
-/* The PerfTraceTimedEvent class is responsible for counting the
+class PerfPauseTimerBase : public StackObj {
+  protected:
+    bool _is_active;
+    BaseTimer* _timer;
+
+  public:
+    inline PerfPauseTimerBase(PerfTraceTimeBase* timer, bool is_on) : _is_active(false), _timer(nullptr) {
+      _is_active = (is_on && timer != nullptr);
+      if (UsePerfData && _is_active) {
+        _timer = timer->timer();
+        _timer->stop(); // pause
+      }
+    }
+
+    inline ~PerfPauseTimerBase() {
+      if (UsePerfData && _is_active) {
+        assert(_timer != nullptr, "");
+        _timer->start(); // resume
+      }
+    }
+};
+
+class PerfPauseTimer : public StackObj {
+  private:
+    PerfPauseTimerBase _elapsed_timer_pause;
+    PerfPauseTimerBase _thread_timer_pause;
+
+  public:
+    inline PerfPauseTimer(PerfTraceTime* timer, bool is_on) :
+                          _elapsed_timer_pause(timer != nullptr ? timer->elapsed_timer() : nullptr, is_on),
+                          _thread_timer_pause(timer != nullptr ? timer->thread_timer() : nullptr, is_on) {}
+};
+
+/* The PerfTraceElapsedTimeEvent class is responsible for counting the
  * occurrence of some event and measuring the elapsed time of
  * the event in two separate PerfCounter instances.
  *
  * Example:
  *
  *    static PerfCounter* my_time_counter = PerfDataManager::create_counter("my.time.counter", PerfData::U_Ticks, CHECK);
  *    static PerfCounter* my_event_counter = PerfDataManager::create_counter("my.event.counter", PerfData::U_Events, CHECK);
  *
  *    {
- *      PerfTraceTimedEvent ptte(my_time_counter, my_event_counter);
+ *      PerfTraceElapsedTimeEvent ptte(my_time_counter, my_event_counter);
  *      // perform the operation you want to count and measure
  *    }
  *
  * Note: use of this class does not need to occur within a guarded
  * block. The UsePerfData guard is used with the implementation
  * of this class.
  *
+ * Similarly, PerfTraceThreadTimeEvent can count the occurrence of some event and measure the thread cpu time of the event.
+ * PerfTraceTimedEvent can count the occurrence of some event and measure both the elapsed time and the thread cpu time of the event.
  */
-class PerfTraceTimedEvent : public PerfTraceTime {
-
+class PerfTraceElapsedTimeEvent: public PerfTraceElapsedTime {
   protected:
     PerfLongCounter* _eventp;
 
   public:
-    inline PerfTraceTimedEvent(PerfLongCounter* timerp, PerfLongCounter* eventp): PerfTraceTime(timerp), _eventp(eventp) {
-      if (!UsePerfData || timerp == nullptr) { return; }
+    inline PerfTraceElapsedTimeEvent(PerfCounter* counter, PerfLongCounter* eventp) : PerfTraceElapsedTime(counter), _eventp(eventp) {
+      if (!UsePerfData || counter == nullptr) return;
       _eventp->inc();
     }
+};
+
+class PerfTraceThreadTimeEvent: public PerfTraceThreadTime {
+  protected:
+    PerfLongCounter* _eventp;
 
+  public:
+    inline PerfTraceThreadTimeEvent(PerfCounter* counter, PerfLongCounter* eventp) : PerfTraceThreadTime(counter), _eventp(eventp) {
+      if (!UsePerfData || counter == nullptr) return;
+      _eventp->inc();
+    }
 };
 
+class PerfTraceTimedEvent : public PerfTraceTime {
+  protected:
+    PerfLongCounter* _eventp;
+
+  public:
+    inline PerfTraceTimedEvent(PerfTickCounters* counters, PerfLongCounter* eventp, bool is_on = true) : PerfTraceTime(counters, is_on), _eventp(eventp) {
+      if (!UsePerfData || !is_on || counters == nullptr) return;
+      _eventp->inc();
+    }
+};
 #endif // SHARE_RUNTIME_PERFDATA_HPP
diff a/src/hotspot/share/runtime/perfData.inline.hpp b/src/hotspot/share/runtime/perfData.inline.hpp
--- a/src/hotspot/share/runtime/perfData.inline.hpp
+++ b/src/hotspot/share/runtime/perfData.inline.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2024, 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.
@@ -25,10 +25,11 @@
 #ifndef SHARE_RUNTIME_PERFDATA_INLINE_HPP
 #define SHARE_RUNTIME_PERFDATA_INLINE_HPP
 
 #include "runtime/perfData.hpp"
 
+#include "services/management.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/growableArray.hpp"
 
 inline int PerfDataList::length() {
   return _set->length();
@@ -48,6 +49,22 @@
   } else {
     return false;
   }
 }
 
+inline jlong PerfTickCounters::elapsed_counter_value_ms() const {
+  return Management::ticks_to_ms(_elapsed_counter->get_value());
+}
+
+inline jlong PerfTickCounters::thread_counter_value_ms() const {
+  return Management::ticks_to_ms(_thread_counter->get_value());
+}
+
+inline jlong PerfTickCounters::elapsed_counter_value_us() const {
+  return Management::ticks_to_us(_elapsed_counter->get_value());
+}
+
+inline jlong PerfTickCounters::thread_counter_value_us() const {
+  return Management::ticks_to_us(_thread_counter->get_value());
+}
+
 #endif // SHARE_RUNTIME_PERFDATA_INLINE_HPP
diff a/src/hotspot/share/runtime/perfDataTypes.hpp b/src/hotspot/share/runtime/perfDataTypes.hpp
--- a/src/hotspot/share/runtime/perfDataTypes.hpp
+++ b/src/hotspot/share/runtime/perfDataTypes.hpp
@@ -34,10 +34,11 @@
 class PerfLongSampleHelper;
 class PerfLongConstant;
 class PerfLongCounter;
 class PerfLongVariable;
 class PerfStringVariable;
+class PerfTickCounters;
 
 typedef PerfLongSampleHelper PerfSampleHelper;
 typedef PerfLongConstant PerfConstant;
 typedef PerfLongCounter PerfCounter;
 typedef PerfLongVariable PerfVariable;
diff a/src/hotspot/share/runtime/runtimeUpcallNop.cpp b/src/hotspot/share/runtime/runtimeUpcallNop.cpp
--- /dev/null
+++ b/src/hotspot/share/runtime/runtimeUpcallNop.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+
+#include "runtime/globals_extension.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/runtimeUpcallNop.hpp"
+#include "runtime/runtimeUpcalls.hpp"
+
+bool RuntimeUpcallNop::_method_filter_result = false;
+
+bool runtimeUpcallNop_register_upcalls()
+{
+  return RuntimeUpcallNop::register_upcalls();
+}
+
+bool RuntimeUpcallNop::register_upcalls()
+{
+  if(AddRuntimeUpcallsNOP == nullptr || FLAG_IS_DEFAULT(AddRuntimeUpcallsNOP)) return true;
+
+  const char* method_entry = "onMethodEntry:";
+  const size_t method_entry_len = strlen(method_entry);
+  const char* method_exit = "onMethodExit:";
+  const size_t method_exit_len = strlen(method_exit);
+
+  const char* filter_all = "all";
+  const size_t filter_all_len = strlen(filter_all);
+  const char* filter_none = "none";
+  const size_t filter_none_len = strlen(filter_none);
+
+  const char* filter_option = nullptr;
+  RuntimeUpcallType upcall_type = RuntimeUpcallType::onMethodEntry;
+  const char* command = AddRuntimeUpcallsNOP == nullptr ? "" : AddRuntimeUpcallsNOP;
+
+  if (strncmp(command, method_entry, method_entry_len) == 0) {
+    filter_option = command + method_entry_len;
+    upcall_type = RuntimeUpcallType::onMethodEntry;
+  } else if (strncmp(command, method_exit, method_exit_len) == 0) {
+    filter_option = command + method_exit_len;
+    upcall_type = RuntimeUpcallType::onMethodExit;
+  } else {
+    ttyLocker ttyl;
+    tty->print_cr("An error occurred during parsing AddRuntimeUpcallsNOP");
+    tty->print_cr("Error! Expected 'onMethodEntry:' or 'onMethodExit:'");
+    return false;
+  }
+
+  assert(filter_option != nullptr, "sanity");
+  if (strncmp(filter_option, filter_all, filter_all_len) == 0) {
+    _method_filter_result = true;
+  } else if (strncmp(filter_option, filter_none, filter_none_len) == 0) {
+    _method_filter_result = false;
+  } else {
+    ttyLocker ttyl;
+    tty->print_cr("An error occurred during parsing AddRuntimeUpcallsNOP");
+    tty->print_cr("Error! Expected 'all' or 'none'");
+    return false;
+  }
+
+  if (RuntimeUpcalls::register_upcall(
+        upcall_type,
+        "nop_method",
+        RuntimeUpcallNop::nop_method,
+        RuntimeUpcallNop::filter_method_callback)) {
+    return true;
+  }
+  return false;
+}
+
+bool RuntimeUpcallNop::filter_method_callback(MethodDetails& method_details)
+{
+  return _method_filter_result;
+}
+
+JRT_ENTRY(void, RuntimeUpcallNop::nop_method(JavaThread* current))
+{
+}
+JRT_END
diff a/src/hotspot/share/runtime/runtimeUpcallNop.hpp b/src/hotspot/share/runtime/runtimeUpcallNop.hpp
--- /dev/null
+++ b/src/hotspot/share/runtime/runtimeUpcallNop.hpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+#ifndef SHARE_RUNTIME_RUNTIME_UPCALL_NOP_HPP
+#define SHARE_RUNTIME_RUNTIME_UPCALL_NOP_HPP
+
+
+#include "memory/allStatic.hpp"
+#include "memory/resourceArea.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/macros.hpp"
+
+class MethodDetails;
+
+class RuntimeUpcallNop : AllStatic {
+private:
+  static bool _method_filter_result;
+public:
+  static bool register_upcalls();
+  static bool filter_method_callback(MethodDetails& method_details);
+  static void nop_method(TRAPS);
+};
+
+#endif // SHARE_RUNTIME_RUNTIME_UPCALL_NOP_HPP
diff a/src/hotspot/share/runtime/runtimeUpcalls.cpp b/src/hotspot/share/runtime/runtimeUpcalls.cpp
--- /dev/null
+++ b/src/hotspot/share/runtime/runtimeUpcalls.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1997, 2024, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/methodDetails.hpp"
+#include "runtime/runtimeUpcalls.hpp"
+
+GrowableArray<RuntimeUpcallInfo*>* RuntimeUpcalls::_upcalls[RuntimeUpcallType::numTypes];
+RuntimeUpcalls::State RuntimeUpcalls::_state = RuntimeUpcalls::Uninitialized;
+
+bool runtimeUpcalls_open_registration() {
+  return RuntimeUpcalls::open_upcall_registration();
+}
+
+bool RuntimeUpcalls::open_upcall_registration() {
+  assert(_state == Uninitialized, "upcalls are already open");
+  for (int i = 0; i < RuntimeUpcallType::numTypes; i++) {
+    RuntimeUpcalls::_upcalls[i] = nullptr;
+  }
+  _state = Open;
+  return true;
+}
+
+void runtimeUpcalls_close_registration() {
+  RuntimeUpcalls::close_upcall_registration();
+}
+
+void RuntimeUpcalls::close_upcall_registration() {
+  assert(_state == Open, "upcalls are not open");
+  _state = Closed;
+}
+
+void RuntimeUpcalls::mark_for_upcalls(RuntimeUpcallType upcall_type, const methodHandle& method) {
+  if (_upcalls[upcall_type] != nullptr) {
+    MethodDetails method_details(method);
+    for(RuntimeUpcallInfo* info : *(_upcalls[upcall_type])) {
+      if(info->includes(method_details)) {
+        switch(upcall_type) {
+          case onMethodEntry: method->set_has_upcall_on_method_entry(true); break;
+          case onMethodExit:  method->set_has_upcall_on_method_exit(true);  break;
+          default:            ShouldNotReachHere();
+        }
+        break;
+      }
+    }
+  }
+}
+
+bool RuntimeUpcalls::register_upcall(RuntimeUpcallType upcall_type, RuntimeUpcallInfo* info) {
+  assert(upcall_type != onMethodExit, "Upcalls on method exit are not supported yet");
+  assert(info != nullptr, "upcall info is null");
+  if (_upcalls[upcall_type] == nullptr) {
+    _upcalls[upcall_type] = new (mtServiceability) GrowableArray<RuntimeUpcallInfo*>(1, mtServiceability);
+  }
+  info->set_index(_upcalls[upcall_type]->length());
+  _upcalls[upcall_type]->append(info);
+  return true;
+}
+
+int RuntimeUpcalls::get_num_upcalls(RuntimeUpcallType upcall_type) {
+  return (_upcalls[upcall_type] == nullptr) ? 0 : _upcalls[upcall_type]->length();
+}
+
+void RuntimeUpcalls::upcall_redirect(RuntimeUpcallType upcall_type, JavaThread* current, Method* method) {
+  MethodDetails method_details(method);
+
+  // This redirection occurs when there are more than one upcalls setup.  Currently each method is marked
+  // to indicate either none, entry and/or exit upcalls (two bits total); then we have to iterate over
+  // all upcalls and test the method details to determine which upcalls to call.  This is not optimal.
+  // One possible optimization is to use more bits to support more upcalls.  The method flags currently use 18
+  // out of 32 bits, so there are still 14 bits available for use.  We could set a limit of say 4-8 entry/exit
+  // upcalls combined, leaving 10-6 bits for other uses.  This still requires a redirect here to determine
+  // which upcalls to call, but it would be more efficient than the current implementation as we'd avoid the
+  // method matching and simply map bits to indexes.
+
+  RuntimeUpcallInfo* upcall = get_first_upcall(upcall_type, method_details);
+  while (upcall != nullptr) {
+    upcall->upcall()(current);
+    upcall = get_next_upcall(upcall_type, method_details, upcall);
+  }
+}
+
+JRT_BLOCK_ENTRY(void, RuntimeUpcalls::on_method_entry_upcall_redirect(JavaThread* current, Method* method)) {
+    RuntimeUpcalls::upcall_redirect(onMethodEntry, current, method);
+}
+JRT_END
+
+JRT_BLOCK_ENTRY(void, RuntimeUpcalls::on_method_exit_upcall_redirect(JavaThread* current, Method* method)) {
+    RuntimeUpcalls::upcall_redirect(onMethodExit, current, method);
+}
+JRT_END
+
+//-------------------------------RuntimeUpcalls---------------------------------------
+
+void RuntimeUpcalls::install_upcalls(const methodHandle& method) {
+  for (int i = 0; i < RuntimeUpcallType::numTypes; i++) {
+      mark_for_upcalls(static_cast<RuntimeUpcallType>(i), method);
+  }
+}
+
+bool RuntimeUpcalls::register_upcall(RuntimeUpcallType upcall_type, const char* upcall_name, RuntimeUpcall upcall, RuntimeUpcallMethodFilterCallback method_filter_callback)
+{
+  assert(upcall_type < numTypes, "invalid upcall type");
+  assert(_state == Open, "upcalls are not open for registration");
+  if (_state != Open) return false;
+  return register_upcall(upcall_type, RuntimeUpcallInfo::create(upcall_name, upcall, method_filter_callback));
+}
+
+RuntimeUpcallInfo* RuntimeUpcalls::get_next_upcall(RuntimeUpcallType upcall_type, MethodDetails& method_details, RuntimeUpcallInfo* prev_upcall_info) {
+  assert(upcall_type < numTypes, "invalid upcall type");
+  if (_upcalls[upcall_type] != nullptr) {
+    // simple case where there's only one upcall
+    if (_upcalls[upcall_type]->length() == 1) {
+      if (prev_upcall_info != nullptr) {
+        return nullptr;
+      }
+      RuntimeUpcallInfo* upcall = _upcalls[upcall_type]->at(0);
+      return upcall->includes(method_details) ? upcall : nullptr;
+    }
+
+    // Resume from where we left off, unless we are the last entry.
+    assert(prev_upcall_info == nullptr || (prev_upcall_info->get_index() >= 0 && prev_upcall_info->get_index() < _upcalls[upcall_type]->length()), "invalid upcall index");
+    int index = (prev_upcall_info != nullptr) ? prev_upcall_info->get_index() + 1 : 0;
+    for (int i = index; i < _upcalls[upcall_type]->length(); i++) {
+      RuntimeUpcallInfo* upcall = _upcalls[upcall_type]->at(i);
+      if (upcall->includes(method_details)) {
+        return upcall;
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+RuntimeUpcallInfo* RuntimeUpcalls::get_first_upcall(RuntimeUpcallType upcall_type, MethodDetails& method_details) {
+  return get_next_upcall(upcall_type, method_details, nullptr);
+}
+
+bool RuntimeUpcalls::does_upcall_need_method_parameter(address upcall_address)
+{
+  // Redirect needs the method parameter for filtering.
+  if((upcall_address == CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_entry_upcall_redirect)) ||
+     (upcall_address == CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_exit_upcall_redirect))) {
+    return true;
+  }
+
+  return false;
+}
+
+address RuntimeUpcalls::on_method_entry_upcall_address()
+{
+  // Optimized case when there's only one upcall (no need to redirect).
+  if(_upcalls[onMethodEntry] != nullptr && _upcalls[onMethodEntry]->length() == 1) {
+    return _upcalls[onMethodEntry]->at(0)->upcall_address();
+  }
+
+  return CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_entry_upcall_redirect);
+}
+
+address RuntimeUpcalls::on_method_exit_upcall_address()
+{
+  // Optimized case when there's only one upcall (no need to redirect).
+  if(_upcalls[onMethodExit] != nullptr && _upcalls[onMethodExit]->length() == 1) {
+    return _upcalls[onMethodExit]->at(0)->upcall_address();
+  }
+
+  return CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_exit_upcall_redirect);
+}
+
+const char* RuntimeUpcalls::get_name_for_upcall_address(address upcall_address)
+{
+  for(int i = 0; i < RuntimeUpcallType::numTypes; i++) {
+    if (_upcalls[i] != nullptr) {
+      for (int j = 0; j < _upcalls[i]->length(); j++) {
+        RuntimeUpcallInfo* upcall = _upcalls[i]->at(j);
+        if (upcall->upcall_address() == upcall_address) {
+          return upcall->upcall_name();
+        }
+      }
+    }
+  }
+  return nullptr;
+}
diff a/src/hotspot/share/runtime/runtimeUpcalls.hpp b/src/hotspot/share/runtime/runtimeUpcalls.hpp
--- /dev/null
+++ b/src/hotspot/share/runtime/runtimeUpcalls.hpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1997, 2024, 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.
+ *
+ */
+
+#ifndef SHARE_RUNTIME_RUNTIME_UPCALLS_HPP
+#define SHARE_RUNTIME_RUNTIME_UPCALLS_HPP
+
+#include "memory/allStatic.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/methodDetails.hpp"
+#include "utilities/macros.hpp"
+
+enum RuntimeUpcallType{
+  onMethodEntry = 0, // Upcalls triggered on method entry AFTER tracing
+  onMethodExit,      // Not implemented yet
+  numTypes
+};
+
+typedef void (*RuntimeUpcall)(JavaThread* current);
+typedef bool (*RuntimeUpcallMethodFilterCallback)(MethodDetails& method);
+
+class RuntimeUpcallInfo: public CHeapObj<mtInternal>{
+  const char* _upcall_name;
+  const RuntimeUpcall _upcall;
+  const RuntimeUpcallMethodFilterCallback _method_filter;
+  address _address;
+  int _index;
+
+  RuntimeUpcallInfo(const char* upcall_name,
+                    const RuntimeUpcall upcall,
+                    const RuntimeUpcallMethodFilterCallback method_filter)
+  : _upcall_name(upcall_name),
+    _upcall(upcall),
+    _method_filter(method_filter),
+    _index(-1) {
+    _address = CAST_FROM_FN_PTR(address, upcall);
+  }
+
+private:
+  friend class RuntimeUpcalls;
+  void set_index(const int index) { _index = index; }
+  int get_index() const { assert(_index >= 0, "invalid index"); return _index; }
+
+public:
+  static RuntimeUpcallInfo* create(const char* upcall_name, const RuntimeUpcall upcall, const RuntimeUpcallMethodFilterCallback method_filter) {
+    assert(upcall_name != nullptr, "upcall name must be provided");
+    assert(upcall != nullptr, "upcall must be provided");
+    assert(method_filter != nullptr, "method filter must be provided");
+    return new RuntimeUpcallInfo(upcall_name, upcall, method_filter);
+  }
+
+  RuntimeUpcall upcall() const { return _upcall; }
+  const char* upcall_name() const { return _upcall_name; }
+  address upcall_address() const { return _address; }
+
+  bool includes(MethodDetails& method_details) const {
+    return _method_filter(method_details);
+  }
+};
+
+class RuntimeUpcalls: AllStatic {
+private:
+
+  enum State {
+    Uninitialized,
+    Open,
+    Closed
+  };
+
+  static GrowableArray<RuntimeUpcallInfo*>* _upcalls[RuntimeUpcallType::numTypes];
+  static State _state;
+
+  static void mark_for_upcalls(RuntimeUpcallType upcall_type, const methodHandle& method);
+  static bool register_upcall(RuntimeUpcallType upcall_type, RuntimeUpcallInfo* info);
+  static void upcall_redirect(RuntimeUpcallType upcall_type, JavaThread* current, Method* method);
+
+  static int  get_num_upcalls(RuntimeUpcallType upcall_type);
+
+  static void on_method_entry_upcall_redirect(JavaThread* current, Method* method);
+  static void on_method_exit_upcall_redirect(JavaThread* current, Method* method);
+
+public:
+
+  static bool               open_upcall_registration();
+  static bool               register_upcall(RuntimeUpcallType upcall_type, const char* upcall_name, RuntimeUpcall upcall, RuntimeUpcallMethodFilterCallback method_filter_callback = nullptr);
+  static void               close_upcall_registration();
+
+  static void               install_upcalls(const methodHandle& method);
+
+  static RuntimeUpcallInfo* get_first_upcall(RuntimeUpcallType upcall_type, MethodDetails& method_details);
+  static RuntimeUpcallInfo* get_next_upcall(RuntimeUpcallType upcall_type, MethodDetails& method_details, RuntimeUpcallInfo* prev_upcall_info = nullptr);
+
+  static address            on_method_entry_upcall_address();
+  static address            on_method_exit_upcall_address();
+
+  static bool               does_upcall_need_method_parameter(address upcall_address);
+
+  static const char*        get_name_for_upcall_address(address upcall_address);
+};
+
+#endif // SHARE_RUNTIME_RUNTIME_UPCALLS_HPP
diff a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -64,19 +64,20 @@
 #include "runtime/init.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/jniHandles.inline.hpp"
-#include "runtime/perfData.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stackWatermarkSet.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/synchronizer.inline.hpp"
 #include "runtime/timerTrace.hpp"
 #include "runtime/vframe.inline.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vm_version.hpp"
+#include "services/management.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/events.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/resourceHash.hpp"
@@ -98,10 +99,16 @@
   SHARED_STUBS_DO(SHARED_STUB_FIELD_DEFINE)
 #undef SHARED_STUB_FIELD_DEFINE
 
 nmethod*            SharedRuntime::_cont_doYield_stub;
 
+PerfTickCounters* SharedRuntime::_perf_resolve_opt_virtual_total_time = nullptr;
+PerfTickCounters* SharedRuntime::_perf_resolve_virtual_total_time     = nullptr;
+PerfTickCounters* SharedRuntime::_perf_resolve_static_total_time      = nullptr;
+PerfTickCounters* SharedRuntime::_perf_handle_wrong_method_total_time = nullptr;
+PerfTickCounters* SharedRuntime::_perf_ic_miss_total_time             = nullptr;
+
 #define SHARED_STUB_NAME_DECLARE(name, type) "Shared Runtime " # name "_blob",
 const char *SharedRuntime::_stub_names[] = {
   SHARED_STUBS_DO(SHARED_STUB_NAME_DECLARE)
 };
 
@@ -166,10 +173,61 @@
   _polling_page_return_handler_blob =
     generate_handler_blob(SharedStubId::polling_page_return_handler_id,
                           CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception));
 
   generate_deopt_blob();
+
+  if (UsePerfData) {
+    EXCEPTION_MARK;
+    NEWPERFTICKCOUNTERS(_perf_resolve_opt_virtual_total_time, SUN_CI, "resovle_opt_virtual_call");
+    NEWPERFTICKCOUNTERS(_perf_resolve_virtual_total_time,     SUN_CI, "resovle_virtual_call");
+    NEWPERFTICKCOUNTERS(_perf_resolve_static_total_time,      SUN_CI, "resovle_static_call");
+    NEWPERFTICKCOUNTERS(_perf_handle_wrong_method_total_time, SUN_CI, "handle_wrong_method");
+    NEWPERFTICKCOUNTERS(_perf_ic_miss_total_time ,            SUN_CI, "ic_miss");
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("SharedRuntime::generate_stubs() failed unexpectedly");
+    }
+  }
+}
+
+static void print_counter_on(outputStream* st, const char* name, PerfTickCounters* counter, uint cnt) {
+  st->print("  %-28s " JLONG_FORMAT_W(6) "us", name, counter->elapsed_counter_value_us());
+  if (TraceThreadTime) {
+    st->print(" (elapsed) " JLONG_FORMAT_W(6) "us (thread)", counter->thread_counter_value_us());
+  }
+  st->print(" / %5d events", cnt);
+  st->cr();
+}
+
+void SharedRuntime::print_counters_on(outputStream* st) {
+  st->print_cr("SharedRuntime:");
+  if (UsePerfData) {
+    print_counter_on(st, "resolve_opt_virtual_call:", _perf_resolve_opt_virtual_total_time, _resolve_opt_virtual_ctr);
+    print_counter_on(st, "resolve_virtual_call:",     _perf_resolve_virtual_total_time,     _resolve_virtual_ctr);
+    print_counter_on(st, "resolve_static_call:",      _perf_resolve_static_total_time,      _resolve_static_ctr);
+    print_counter_on(st, "handle_wrong_method:",      _perf_handle_wrong_method_total_time, _wrong_method_ctr);
+    print_counter_on(st, "ic_miss:",                  _perf_ic_miss_total_time,             _ic_miss_ctr);
+
+    jlong total_elapsed_time_us = Management::ticks_to_us(_perf_resolve_opt_virtual_total_time->elapsed_counter_value() +
+                                                          _perf_resolve_virtual_total_time->elapsed_counter_value() +
+                                                          _perf_resolve_static_total_time->elapsed_counter_value() +
+                                                          _perf_handle_wrong_method_total_time->elapsed_counter_value() +
+                                                          _perf_ic_miss_total_time->elapsed_counter_value());
+    st->print("Total:                      " JLONG_FORMAT_W(5) "us", total_elapsed_time_us);
+    if (TraceThreadTime) {
+      jlong total_thread_time_us = Management::ticks_to_us(_perf_resolve_opt_virtual_total_time->thread_counter_value() +
+                                                           _perf_resolve_virtual_total_time->thread_counter_value() +
+                                                           _perf_resolve_static_total_time->thread_counter_value() +
+                                                           _perf_handle_wrong_method_total_time->thread_counter_value() +
+                                                           _perf_ic_miss_total_time->thread_counter_value());
+      st->print(" (elapsed) " JLONG_FORMAT_W(5) "us (thread)", total_thread_time_us);
+
+    }
+    st->cr();
+  } else {
+    st->print_cr("  no data (UsePerfData is turned off)");
+  }
 }
 
 #if INCLUDE_JFR
 //------------------------------generate jfr runtime stubs ------
 void SharedRuntime::generate_jfr_stubs() {
@@ -185,17 +243,18 @@
 
 #include <math.h>
 
 // Implementation of SharedRuntime
 
-#ifndef PRODUCT
 // For statistics
 uint SharedRuntime::_ic_miss_ctr = 0;
 uint SharedRuntime::_wrong_method_ctr = 0;
 uint SharedRuntime::_resolve_static_ctr = 0;
 uint SharedRuntime::_resolve_virtual_ctr = 0;
 uint SharedRuntime::_resolve_opt_virtual_ctr = 0;
+
+#ifndef PRODUCT
 uint SharedRuntime::_implicit_null_throws = 0;
 uint SharedRuntime::_implicit_div0_throws = 0;
 
 int64_t SharedRuntime::_nof_normal_calls = 0;
 int64_t SharedRuntime::_nof_inlined_calls = 0;
@@ -245,22 +304,22 @@
   if (_ICmiss_index >= maxICmiss_count) _ICmiss_index = maxICmiss_count - 1;
   _ICmiss_at[index] = at;
   _ICmiss_count[index] = 1;
 }
 
-void SharedRuntime::print_ic_miss_histogram() {
+void SharedRuntime::print_ic_miss_histogram_on(outputStream* st) {
   if (ICMissHistogram) {
-    tty->print_cr("IC Miss Histogram:");
+    st->print_cr("IC Miss Histogram:");
     int tot_misses = 0;
     for (int i = 0; i < _ICmiss_index; i++) {
-      tty->print_cr("  at: " INTPTR_FORMAT "  nof: %d", p2i(_ICmiss_at[i]), _ICmiss_count[i]);
+      st->print_cr("  at: " INTPTR_FORMAT "  nof: %d", p2i(_ICmiss_at[i]), _ICmiss_count[i]);
       tot_misses += _ICmiss_count[i];
     }
-    tty->print_cr("Total IC misses: %7d", tot_misses);
+    st->print_cr("Total IC misses: %7d", tot_misses);
   }
 }
-#endif // PRODUCT
+#endif // !PRODUCT
 
 
 JRT_LEAF(jlong, SharedRuntime::lmul(jlong y, jlong x))
   return x * y;
 JRT_END
@@ -723,10 +782,22 @@
     // an error. Our method could have been redefined just after we
     // fetched the Method* from the constant pool.
     ResourceMark rm;
     log_trace(redefine, class, obsolete)("calling obsolete method '%s'", method->name_and_sig_as_C_string());
   }
+
+  LogStreamHandle(Trace, interpreter, bytecode) log;
+  if (log.is_enabled()) {
+    ResourceMark rm;
+    log.print("method entry: " INTPTR_FORMAT " %s %s%s%s%s",
+              p2i(thread),
+              (method->is_static() ? "static" : "virtual"),
+              method->name_and_sig_as_C_string(),
+              (method->is_native() ? " native" : ""),
+              (thread->class_being_initialized() != nullptr ? " clinit" : ""),
+              (method->method_holder()->is_initialized() ? "" : " being_initialized"));
+  }
   return 0;
 JRT_END
 
 // ret_pc points into caller; we are returning caller's exception handler
 // for given exception
@@ -1362,17 +1433,17 @@
          (!is_virtual && invoke_code == Bytecodes::_invokedynamic) ||
          ( is_virtual && invoke_code != Bytecodes::_invokestatic ), "inconsistent bytecode");
 
   assert(!caller_nm->is_unloading(), "It should not be unloading");
 
-#ifndef PRODUCT
   // tracing/debugging/statistics
   uint *addr = (is_optimized) ? (&_resolve_opt_virtual_ctr) :
                  (is_virtual) ? (&_resolve_virtual_ctr) :
                                 (&_resolve_static_ctr);
   Atomic::inc(addr);
 
+#ifndef PRODUCT
   if (TraceCallFixup) {
     ResourceMark rm(current);
     tty->print("resolving %s%s (%s) call to",
                (is_optimized) ? "optimized " : "", (is_virtual) ? "virtual" : "static",
                Bytecodes::name(invoke_code));
@@ -1424,10 +1495,12 @@
   return callee_method;
 }
 
 // Inline caches exist only in compiled code
 JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_ic_miss(JavaThread* current))
+  PerfTraceTime timer(_perf_ic_miss_total_time);
+
 #ifdef ASSERT
   RegisterMap reg_map(current,
                       RegisterMap::UpdateMap::skip,
                       RegisterMap::ProcessFrames::include,
                       RegisterMap::WalkContinuation::skip);
@@ -1448,10 +1521,12 @@
 JRT_END
 
 
 // Handle call site that has been made non-entrant
 JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method(JavaThread* current))
+  PerfTraceTime timer(_perf_handle_wrong_method_total_time);
+
   // 6243940 We might end up in here if the callee is deoptimized
   // as we race to call it.  We don't want to take a safepoint if
   // the caller was interpreted because the caller frame will look
   // interpreted to the stack walkers and arguments are now
   // "compiled" so it is much better to make this transition
@@ -1500,10 +1575,12 @@
   return get_resolved_entry(current, callee_method);
 JRT_END
 
 // Handle abstract method call
 JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_abstract(JavaThread* current))
+  PerfTraceTime timer(_perf_handle_wrong_method_total_time);
+
   // Verbose error message for AbstractMethodError.
   // Get the called method from the invoke bytecode.
   vframeStream vfst(current, true);
   assert(!vfst.at_end(), "Java frame must exist");
   methodHandle caller(current, vfst.method());
@@ -1546,10 +1623,12 @@
   return callee_method->verified_code_entry();
 }
 
 // resolve a static call and patch code
 JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_static_call_C(JavaThread* current ))
+  PerfTraceTime timer(_perf_resolve_static_total_time);
+
   methodHandle callee_method;
   bool enter_special = false;
   JRT_BLOCK
     callee_method = SharedRuntime::resolve_helper(false, false, CHECK_NULL);
     current->set_vm_result_2(callee_method());
@@ -1558,10 +1637,12 @@
   return get_resolved_entry(current, callee_method);
 JRT_END
 
 // resolve virtual call and update inline cache to monomorphic
 JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_virtual_call_C(JavaThread* current))
+  PerfTraceTime timer(_perf_resolve_virtual_total_time);
+
   methodHandle callee_method;
   JRT_BLOCK
     callee_method = SharedRuntime::resolve_helper(true, false, CHECK_NULL);
     current->set_vm_result_2(callee_method());
   JRT_BLOCK_END
@@ -1571,10 +1652,12 @@
 
 
 // Resolve a virtual call that can be statically bound (e.g., always
 // monomorphic, so it has no inline cache).  Patch code to resolved target.
 JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_opt_virtual_call_C(JavaThread* current))
+  PerfTraceTime timer(_perf_resolve_opt_virtual_total_time);
+
   methodHandle callee_method;
   JRT_BLOCK
     callee_method = SharedRuntime::resolve_helper(true, true, CHECK_NULL);
     current->set_vm_result_2(callee_method());
   JRT_BLOCK_END
@@ -1592,13 +1675,13 @@
   // receivers for non-static calls
   Handle receiver = find_callee_info(bc, call_info, CHECK_(methodHandle()));
 
   methodHandle callee_method(current, call_info.selected_method());
 
-#ifndef PRODUCT
   Atomic::inc(&_ic_miss_ctr);
 
+#ifndef PRODUCT
   // Statistics & Tracing
   if (TraceCallFixup) {
     ResourceMark rm(current);
     tty->print("IC miss (%s) call to", Bytecodes::name(bc));
     callee_method->print_short_name(tty);
@@ -1717,14 +1800,13 @@
     }
   }
 
   methodHandle callee_method = find_callee_method(CHECK_(methodHandle()));
 
-
-#ifndef PRODUCT
   Atomic::inc(&_wrong_method_ctr);
 
+#ifndef PRODUCT
   if (TraceCallFixup) {
     ResourceMark rm(current);
     tty->print("handle_wrong_method reresolving call to");
     callee_method->print_short_name(tty);
     tty->print_cr(" code: " INTPTR_FORMAT, p2i(callee_method->code()));
@@ -2018,49 +2100,51 @@
 
 void SharedRuntime::print_statistics() {
   ttyLocker ttyl;
   if (xtty != nullptr)  xtty->head("statistics type='SharedRuntime'");
 
-  SharedRuntime::print_ic_miss_histogram();
-
-  // Dump the JRT_ENTRY counters
-  if (_new_instance_ctr) tty->print_cr("%5u new instance requires GC", _new_instance_ctr);
-  if (_new_array_ctr) tty->print_cr("%5u new array requires GC", _new_array_ctr);
-  if (_multi2_ctr) tty->print_cr("%5u multianewarray 2 dim", _multi2_ctr);
-  if (_multi3_ctr) tty->print_cr("%5u multianewarray 3 dim", _multi3_ctr);
-  if (_multi4_ctr) tty->print_cr("%5u multianewarray 4 dim", _multi4_ctr);
-  if (_multi5_ctr) tty->print_cr("%5u multianewarray 5 dim", _multi5_ctr);
-
-  tty->print_cr("%5u inline cache miss in compiled", _ic_miss_ctr);
-  tty->print_cr("%5u wrong method", _wrong_method_ctr);
-  tty->print_cr("%5u unresolved static call site", _resolve_static_ctr);
-  tty->print_cr("%5u unresolved virtual call site", _resolve_virtual_ctr);
-  tty->print_cr("%5u unresolved opt virtual call site", _resolve_opt_virtual_ctr);
-
-  if (_mon_enter_stub_ctr) tty->print_cr("%5u monitor enter stub", _mon_enter_stub_ctr);
-  if (_mon_exit_stub_ctr) tty->print_cr("%5u monitor exit stub", _mon_exit_stub_ctr);
-  if (_mon_enter_ctr) tty->print_cr("%5u monitor enter slow", _mon_enter_ctr);
-  if (_mon_exit_ctr) tty->print_cr("%5u monitor exit slow", _mon_exit_ctr);
-  if (_partial_subtype_ctr) tty->print_cr("%5u slow partial subtype", _partial_subtype_ctr);
-  if (_jbyte_array_copy_ctr) tty->print_cr("%5u byte array copies", _jbyte_array_copy_ctr);
-  if (_jshort_array_copy_ctr) tty->print_cr("%5u short array copies", _jshort_array_copy_ctr);
-  if (_jint_array_copy_ctr) tty->print_cr("%5u int array copies", _jint_array_copy_ctr);
-  if (_jlong_array_copy_ctr) tty->print_cr("%5u long array copies", _jlong_array_copy_ctr);
-  if (_oop_array_copy_ctr) tty->print_cr("%5u oop array copies", _oop_array_copy_ctr);
-  if (_checkcast_array_copy_ctr) tty->print_cr("%5u checkcast array copies", _checkcast_array_copy_ctr);
-  if (_unsafe_array_copy_ctr) tty->print_cr("%5u unsafe array copies", _unsafe_array_copy_ctr);
-  if (_generic_array_copy_ctr) tty->print_cr("%5u generic array copies", _generic_array_copy_ctr);
-  if (_slow_array_copy_ctr) tty->print_cr("%5u slow array copies", _slow_array_copy_ctr);
-  if (_find_handler_ctr) tty->print_cr("%5u find exception handler", _find_handler_ctr);
-  if (_rethrow_ctr) tty->print_cr("%5u rethrow handler", _rethrow_ctr);
-  if (_unsafe_set_memory_ctr) tty->print_cr("%5u unsafe set memorys", _unsafe_set_memory_ctr);
-
-  AdapterHandlerLibrary::print_statistics();
+  SharedRuntime::print_ic_miss_histogram_on(tty);
+  SharedRuntime::print_counters_on(tty);
+  AdapterHandlerLibrary::print_statistics_on(tty);
 
   if (xtty != nullptr)  xtty->tail("statistics");
 }
 
+//void SharedRuntime::print_counters_on(outputStream* st) {
+//  // Dump the JRT_ENTRY counters
+//  if (_new_instance_ctr) st->print_cr("%5u new instance requires GC", _new_instance_ctr);
+//  if (_new_array_ctr)    st->print_cr("%5u new array requires GC", _new_array_ctr);
+//  if (_multi2_ctr)       st->print_cr("%5u multianewarray 2 dim", _multi2_ctr);
+//  if (_multi3_ctr)       st->print_cr("%5u multianewarray 3 dim", _multi3_ctr);
+//  if (_multi4_ctr)       st->print_cr("%5u multianewarray 4 dim", _multi4_ctr);
+//  if (_multi5_ctr)       st->print_cr("%5u multianewarray 5 dim", _multi5_ctr);
+//
+//  st->print_cr("%5u inline cache miss in compiled", _ic_miss_ctr);
+//  st->print_cr("%5u wrong method", _wrong_method_ctr);
+//  st->print_cr("%5u unresolved static call site", _resolve_static_ctr);
+//  st->print_cr("%5u unresolved virtual call site", _resolve_virtual_ctr);
+//  st->print_cr("%5u unresolved opt virtual call site", _resolve_opt_virtual_ctr);
+//
+//  if (_mon_enter_stub_ctr)       st->print_cr("%5u monitor enter stub", _mon_enter_stub_ctr);
+//  if (_mon_exit_stub_ctr)        st->print_cr("%5u monitor exit stub", _mon_exit_stub_ctr);
+//  if (_mon_enter_ctr)            st->print_cr("%5u monitor enter slow", _mon_enter_ctr);
+//  if (_mon_exit_ctr)             st->print_cr("%5u monitor exit slow", _mon_exit_ctr);
+//  if (_partial_subtype_ctr)      st->print_cr("%5u slow partial subtype", _partial_subtype_ctr);
+//  if (_jbyte_array_copy_ctr)     st->print_cr("%5u byte array copies", _jbyte_array_copy_ctr);
+//  if (_jshort_array_copy_ctr)    st->print_cr("%5u short array copies", _jshort_array_copy_ctr);
+//  if (_jint_array_copy_ctr)      st->print_cr("%5u int array copies", _jint_array_copy_ctr);
+//  if (_jlong_array_copy_ctr)     st->print_cr("%5u long array copies", _jlong_array_copy_ctr);
+//  if (_oop_array_copy_ctr)       st->print_cr("%5u oop array copies", _oop_array_copy_ctr);
+//  if (_checkcast_array_copy_ctr) st->print_cr("%5u checkcast array copies", _checkcast_array_copy_ctr);
+//  if (_unsafe_array_copy_ctr)    st->print_cr("%5u unsafe array copies", _unsafe_array_copy_ctr);
+//  if (_generic_array_copy_ctr)   st->print_cr("%5u generic array copies", _generic_array_copy_ctr);
+//  if (_slow_array_copy_ctr)      st->print_cr("%5u slow array copies", _slow_array_copy_ctr);
+//  if (_find_handler_ctr)         st->print_cr("%5u find exception handler", _find_handler_ctr);
+//  if (_rethrow_ctr)              st->print_cr("%5u rethrow handler", _rethrow_ctr);
+//  if (_unsafe_set_memory_ctr) tty->print_cr("%5u unsafe set memorys", _unsafe_set_memory_ctr);
+//}
+
 inline double percent(int64_t x, int64_t y) {
   return 100.0 * (double)x / (double)MAX2(y, (int64_t)1);
 }
 
 class MethodArityHistogram {
@@ -2141,11 +2225,11 @@
 uint64_t MethodArityHistogram::_total_compiled_calls;
 uint64_t MethodArityHistogram::_max_compiled_calls_per_method;
 int MethodArityHistogram::_max_arity;
 int MethodArityHistogram::_max_size;
 
-void SharedRuntime::print_call_statistics(uint64_t comp_total) {
+void SharedRuntime::print_call_statistics_on(outputStream* st) {
   tty->print_cr("Calls from compiled code:");
   int64_t total  = _nof_normal_calls + _nof_interface_calls + _nof_static_calls;
   int64_t mono_c = _nof_normal_calls - _nof_megamorphic_calls;
   int64_t mono_i = _nof_interface_calls;
   tty->print_cr("\t" INT64_FORMAT_W(12) " (100%%)  total non-inlined   ", total);
@@ -2394,22 +2478,22 @@
   }
   return nullptr;
 }
 
 #ifndef PRODUCT
-static void print_table_statistics() {
+void AdapterHandlerLibrary::print_statistics_on(outputStream* st) {
   auto size = [&] (AdapterFingerPrint* key, AdapterHandlerEntry* a) {
     return sizeof(*key) + sizeof(*a);
   };
   TableStatistics ts = _adapter_handler_table->statistics_calculate(size);
-  ts.print(tty, "AdapterHandlerTable");
-  tty->print_cr("AdapterHandlerTable (table_size=%d, entries=%d)",
-                _adapter_handler_table->table_size(), _adapter_handler_table->number_of_entries());
-  tty->print_cr("AdapterHandlerTable: lookups %d equals %d hits %d compact %d",
-                _lookups, _equals, _hits, _compact);
+  ts.print(st, "AdapterHandlerTable");
+  st->print_cr("AdapterHandlerTable (table_size=%d, entries=%d)",
+               _adapter_handler_table->table_size(), _adapter_handler_table->number_of_entries());
+  st->print_cr("AdapterHandlerTable: lookups %d equals %d hits %d compact %d",
+               _lookups, _equals, _hits, _compact);
 }
-#endif
+#endif // !PRODUCT
 
 // ---------------------------------------------------------------------------
 // Implementation of AdapterHandlerLibrary
 AdapterHandlerEntry* AdapterHandlerLibrary::_abstract_method_handler = nullptr;
 AdapterHandlerEntry* AdapterHandlerLibrary::_no_arg_handler = nullptr;
@@ -3093,18 +3177,10 @@
     st->print(" c2iNCI: " INTPTR_FORMAT, p2i(get_c2i_no_clinit_check_entry()));
   }
   st->cr();
 }
 
-#ifndef PRODUCT
-
-void AdapterHandlerLibrary::print_statistics() {
-  print_table_statistics();
-}
-
-#endif /* PRODUCT */
-
 JRT_LEAF(void, SharedRuntime::enable_stack_reserved_zone(JavaThread* current))
   assert(current == JavaThread::current(), "pre-condition");
   StackOverflow* overflow_state = current->stack_overflow_state();
   overflow_state->enable_stack_reserved_zone(/*check_if_disabled*/true);
   overflow_state->set_reserved_stack_activation(current->stack_base());
diff a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp
--- a/src/hotspot/share/runtime/sharedRuntime.hpp
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp
@@ -565,10 +565,25 @@
   static address handle_wrong_method_abstract(JavaThread* current);
   static address handle_wrong_method_ic_miss(JavaThread* current);
 
   static address handle_unsafe_access(JavaThread* thread, address next_pc);
 
+ private:
+  static PerfTickCounters* _perf_resolve_opt_virtual_total_time;
+  static PerfTickCounters* _perf_resolve_virtual_total_time;
+  static PerfTickCounters* _perf_resolve_static_total_time;
+  static PerfTickCounters* _perf_handle_wrong_method_total_time;
+  static PerfTickCounters* _perf_ic_miss_total_time;
+ public:
+  static uint _ic_miss_ctr;                      // total # of IC misses
+  static uint _wrong_method_ctr;
+  static uint _resolve_static_ctr;
+  static uint _resolve_virtual_ctr;
+  static uint _resolve_opt_virtual_ctr;
+
+  static void print_counters_on(outputStream* st);
+
 #ifndef PRODUCT
 
   // Collect and print inline cache miss statistics
  private:
   enum { maxICmiss_count = 100 };
@@ -576,15 +591,10 @@
   static int     _ICmiss_count[maxICmiss_count]; // miss counts
   static address _ICmiss_at[maxICmiss_count];    // miss addresses
   static void trace_ic_miss(address at);
 
  public:
-  static uint _ic_miss_ctr;                      // total # of IC misses
-  static uint _wrong_method_ctr;
-  static uint _resolve_static_ctr;
-  static uint _resolve_virtual_ctr;
-  static uint _resolve_opt_virtual_ctr;
   static uint _implicit_null_throws;
   static uint _implicit_div0_throws;
 
   static uint _jbyte_array_copy_ctr;       // Slow-path byte array copy
   static uint _jshort_array_copy_ctr;      // Slow-path short array copy
@@ -624,13 +634,13 @@
   static address nof_inlined_calls_addr()               { return (address)&_nof_inlined_calls; }
   static address nof_static_calls_addr()                { return (address)&_nof_static_calls; }
   static address nof_inlined_static_calls_addr()        { return (address)&_nof_inlined_static_calls; }
   static address nof_interface_calls_addr()             { return (address)&_nof_interface_calls; }
   static address nof_inlined_interface_calls_addr()     { return (address)&_nof_inlined_interface_calls; }
-  static void print_call_statistics(uint64_t comp_total);
-  static void print_ic_miss_histogram();
 
+  static void print_call_statistics_on(outputStream* st);
+  static void print_ic_miss_histogram_on(outputStream* st);
 #endif // PRODUCT
 
   static void print_statistics() PRODUCT_RETURN;
 };
 
@@ -751,11 +761,11 @@
 
   static void print_handler(const CodeBlob* b) { print_handler_on(tty, b); }
   static void print_handler_on(outputStream* st, const CodeBlob* b);
   static bool contains(const CodeBlob* b);
 #ifndef PRODUCT
-  static void print_statistics();
+  static void print_statistics_on(outputStream* st);
 #endif // PRODUCT
 
 };
 
 #endif // SHARE_RUNTIME_SHAREDRUNTIME_HPP
diff a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp
--- a/src/hotspot/share/runtime/stubDeclarations.hpp
+++ b/src/hotspot/share/runtime/stubDeclarations.hpp
@@ -158,10 +158,11 @@
   do_stub(monitor_notify, 0, false, false)                             \
   do_stub(monitor_notifyAll, 0, false, false)                          \
   do_stub(rethrow, 2, true, true)                                      \
   do_stub(slow_arraycopy, 0, false, false)                             \
   do_stub(register_finalizer, 0, false, false)                         \
+  do_stub(class_init_barrier, 0, false, false)                         \
 
 #else
 #define C2_STUBS_DO(do_blob, do_stub, do_jvmti_stub)
 #endif
 
diff a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp
--- a/src/hotspot/share/runtime/stubRoutines.cpp
+++ b/src/hotspot/share/runtime/stubRoutines.cpp
@@ -226,11 +226,10 @@
     }
   }
   return nullptr;
 }
 
-
 static BufferBlob* initialize_stubs(StubCodeGenerator::StubsKind kind,
                                     int code_size, int max_aligned_stubs,
                                     const char* timer_msg,
                                     const char* buffer_name,
                                     const char* assert_msg) {
diff a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp
--- a/src/hotspot/share/runtime/thread.cpp
+++ b/src/hotspot/share/runtime/thread.cpp
@@ -138,10 +138,21 @@
     // If the main thread creates other threads before the barrier set that is an error.
     assert(Thread::current_or_null() == nullptr, "creating thread before barrier set");
   }
 
   MACOS_AARCH64_ONLY(DEBUG_ONLY(_wx_init = false));
+
+  _profile_vm_locks = false;
+  _profile_vm_calls = false;
+  _profile_vm_ops   = false;
+  _profile_rt_calls = false;
+  _profile_upcalls  = false;
+
+  _all_bc_counter_value = 0;
+  _clinit_bc_counter_value = 0;
+
+  _current_rt_call_timer = nullptr;
 }
 
 #ifdef ASSERT
 address Thread::stack_base() const {
   // Note: can't report Thread::name() here as that can require a ResourceMark which we
@@ -608,5 +619,17 @@
   // Conceptually we need a #loadstore|#storestore "release" MEMBAR before
   // the ST of 0 into the lock-word which releases the lock, so fence
   // more than covers this on all platforms.
   *adr = 0;
 }
+
+const char* ProfileVMCallContext::name(PerfTraceTime* t) {
+  return t->name();
+}
+
+int ProfileVMCallContext::_perf_nested_runtime_calls_count = 0;
+
+void ProfileVMCallContext::notify_nested_rt_call(PerfTraceTime* outer_timer, PerfTraceTime* inner_timer) {
+  log_debug(init)("Nested runtime call: inner=%s outer=%s", inner_timer->name(), outer_timer->name());
+  Atomic::inc(&ProfileVMCallContext::_perf_nested_runtime_calls_count);
+}
+
diff a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp
--- a/src/hotspot/share/runtime/thread.hpp
+++ b/src/hotspot/share/runtime/thread.hpp
@@ -59,10 +59,12 @@
 class ThreadsList;
 class ThreadsSMRSupport;
 class VMErrorCallback;
 
 
+class PerfTraceTime;
+
 DEBUG_ONLY(class ResourceMark;)
 
 class WorkerThread;
 
 class JavaThread;
@@ -629,10 +631,108 @@
     return cur != nullptr && cur->in_asgct();
   }
 
  private:
   VMErrorCallback* _vm_error_callbacks;
+
+  bool  _profile_vm_locks;
+  bool  _profile_vm_calls;
+  bool  _profile_vm_ops;
+  bool  _profile_rt_calls;
+  bool  _profile_upcalls;
+
+  jlong    _all_bc_counter_value;
+  jlong _clinit_bc_counter_value;
+
+  PerfTraceTime* _current_rt_call_timer;
+ public:
+  bool     profile_vm_locks() const { return _profile_vm_locks; }
+  void set_profile_vm_locks(bool v) { _profile_vm_locks = v; }
+
+  bool     profile_vm_calls() const { return _profile_vm_calls; }
+  void set_profile_vm_calls(bool v) { _profile_vm_calls = v; }
+
+  bool     profile_vm_ops() const { return _profile_vm_ops; }
+  void set_profile_vm_ops(bool v) { _profile_vm_ops = v; }
+
+  bool     profile_rt_calls() const { return _profile_rt_calls; }
+  void set_profile_rt_calls(bool v) { _profile_rt_calls = v; }
+
+  bool     profile_upcalls() const { return _profile_upcalls; }
+  void set_profile_upcalls(bool v) { _profile_upcalls = v; }
+
+  PerfTraceTime*     current_rt_call_timer() const           { return _current_rt_call_timer;            }
+  void           set_current_rt_call_timer(PerfTraceTime* c) {        _current_rt_call_timer = c;        }
+  bool           has_current_rt_call_timer() const           { return _current_rt_call_timer != nullptr; }
+
+  bool do_profile_rt_call() const {
+    return ProfileRuntimeCalls && profile_rt_calls() && !has_current_rt_call_timer();
+  }
+
+  jlong        bc_counter_value() const { return    _all_bc_counter_value; }
+
+  jlong clinit_bc_counter_value() const { return _clinit_bc_counter_value; }
+
+  void inc_clinit_bc_counter_value(jlong l) { _clinit_bc_counter_value += l; }
+
+  static ByteSize bc_counter_offset() { return byte_offset_of(Thread, _all_bc_counter_value); }
+};
+
+class ProfileVMCallContext : StackObj {
+ private:
+  Thread* _thread;
+  bool _enabled;
+  PerfTraceTime* _timer;
+
+  static int _perf_nested_runtime_calls_count;
+
+  static const char* name(PerfTraceTime* t);
+  static void notify_nested_rt_call(PerfTraceTime* current, PerfTraceTime* inner_timer);
+ public:
+  inline ProfileVMCallContext(Thread* current, PerfTraceTime* timer, bool is_on)
+  : _thread(current), _enabled(is_on), _timer(timer) {
+    if (_enabled) {
+      assert(timer != nullptr, "");
+      assert(_thread->current_rt_call_timer() == nullptr, "%s", name(_thread->current_rt_call_timer()));
+      _thread->set_current_rt_call_timer(timer);
+    } else if (current->profile_rt_calls()) {
+      notify_nested_rt_call(current->current_rt_call_timer(), timer);
+    }
+  }
+
+  inline ~ProfileVMCallContext() {
+    if (_enabled) {
+      assert(_timer == _thread->current_rt_call_timer(),
+             "%s vs %s", name(_timer), name(_thread->current_rt_call_timer()));
+      _thread->set_current_rt_call_timer(nullptr);
+    }
+  }
+
+  static int nested_runtime_calls_count() { return _perf_nested_runtime_calls_count; };
+};
+
+class PauseRuntimeCallProfiling : public StackObj {
+ protected:
+  Thread* _thread;
+  bool _enabled;
+  PerfTraceTime* _timer;
+
+ public:
+  inline PauseRuntimeCallProfiling(Thread* current, bool is_on)
+  : _thread(current), _enabled(is_on), _timer(nullptr) {
+    if (_enabled) {
+      _timer = _thread->current_rt_call_timer();
+      _thread->set_current_rt_call_timer(nullptr);
+    }
+  }
+
+  inline ~PauseRuntimeCallProfiling () {
+    if (_enabled) {
+      guarantee(_thread->current_rt_call_timer() == nullptr, "");
+      _thread->set_current_rt_call_timer(_timer); // restore
+    }
+  }
 };
 
 class ThreadInAsgct {
  private:
   Thread* _thread;
diff a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -27,26 +27,32 @@
 #include "cds/aotLinkedClassBulkLoader.hpp"
 #include "cds/cds_globals.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/heapShared.hpp"
 #include "cds/metaspaceShared.hpp"
+#include "cds/methodProfiler.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/javaThreadStatus.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
+#include "code/SCCache.hpp"
+#include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compileTask.hpp"
 #include "compiler/compilerThread.hpp"
+#include "compiler/precompiler.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetNMethod.hpp"
 #include "gc/shared/gcVMOperations.hpp"
 #include "gc/shared/oopStorage.hpp"
 #include "gc/shared/oopStorageSet.hpp"
 #include "gc/shared/stringdedup/stringDedup.hpp"
+#include "interpreter/interpreterRuntime.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "jvm.h"
 #include "jvmtifiles/jvmtiEnv.hpp"
 #include "logging/log.hpp"
 #include "logging/logAsyncWriter.hpp"
@@ -108,12 +114,16 @@
 #include "utilities/vmError.hpp"
 #if INCLUDE_JVMCI
 #include "jvmci/jvmci.hpp"
 #include "jvmci/jvmciEnv.hpp"
 #endif
+#ifdef COMPILER1
+#include "c1/c1_Runtime1.hpp"
+#endif
 #ifdef COMPILER2
 #include "opto/idealGraphPrinter.hpp"
+#include "opto/runtime.hpp"
 #endif
 #if INCLUDE_JFR
 #include "jfr/jfr.hpp"
 #endif
 
@@ -324,10 +334,34 @@
   if (result.get_jint() != JNI_OK) {
     vm_exit_during_initialization(); // no message or exception
   }
 
   universe_post_module_init();
+
+#if 0
+  if (CDSConfig::is_using_aot_linked_classes()) {
+    AOTLinkedClassBulkLoader::load_non_javabase_boot_classes(THREAD); 
+    if (CDSConfig::is_using_full_module_graph()) {
+      assert(SystemDictionary::java_platform_loader() != nullptr, "must be");
+      assert(SystemDictionary::java_system_loader() != nullptr,   "must be");
+      AOTLinkedClassBulkLoader::load_platform_classes(THREAD);
+      AOTLinkedClassBulkLoader::load_app_classes(THREAD);
+    } else {
+      // Special case -- we assume that the final archive has the same module graph
+      // as the training run.
+      // AOTLinkedClassBulkLoader will be called for the platform/system loaders
+      // inside SystemDictionary::compute_java_loaders().
+      assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+      assert(SystemDictionary::java_platform_loader() == nullptr, "must be");
+      assert(SystemDictionary::java_system_loader() == nullptr,   "must be");
+    }
+  }
+
+#ifndef PRODUCT
+  HeapShared::initialize_test_class_from_archive(THREAD);
+#endif
+#endif
 }
 
 // Phase 3. final setup - set security manager, system class loader and TCCL
 //
 //     This will instantiate and set the security manager, set the system class
@@ -410,10 +444,42 @@
   initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK);
   initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK);
   initialize_class(vmSymbols::java_lang_InternalError(), CHECK);
 }
 
+bool Threads::initialize_compilation(TRAPS) {
+  // initialize compiler(s)
+  bool force_JVMCI_initialization = false;
+
+#if defined(COMPILER1) || COMPILER2_OR_JVMCI
+  bool init_compilation = true;
+#if INCLUDE_JVMCI
+  if (EnableJVMCI) {
+    // Initialize JVMCI eagerly when it is explicitly requested.
+    // Or when JVMCILibDumpJNIConfig or JVMCIPrintProperties is enabled.
+    force_JVMCI_initialization = EagerJVMCI || JVMCIPrintProperties || JVMCILibDumpJNIConfig;
+    if (!force_JVMCI_initialization && UseJVMCICompiler && !UseJVMCINativeLibrary && (!UseInterpreter || !BackgroundCompilation)) {
+      // Force initialization of jarjvmci otherwise requests for blocking
+      // compilations will not actually block until jarjvmci is initialized.
+      force_JVMCI_initialization = true;
+    }
+    if (JVMCIPrintProperties || JVMCILibDumpJNIConfig) {
+      // Both JVMCILibDumpJNIConfig and JVMCIPrintProperties exit the VM
+      // so compilation should be disabled. This prevents dumping or
+      // printing from happening more than once.
+      init_compilation = false;
+    }
+  }
+#endif
+  if (init_compilation) {
+    CompileBroker::compilation_init(CHECK_false);
+  }
+#endif
+
+  return force_JVMCI_initialization;
+}
+
 void Threads::initialize_jsr292_core_classes(TRAPS) {
   TraceTime timer("Initialize java.lang.invoke classes", TRACETIME_LOG(Info, startuptime));
 
   initialize_class(vmSymbols::java_lang_invoke_MethodHandle(), CHECK);
   initialize_class(vmSymbols::java_lang_invoke_ResolvedMethodName(), CHECK);
@@ -546,10 +612,12 @@
   main_thread->record_stack_base_and_size();
   main_thread->register_thread_stack_with_NMT();
   main_thread->set_active_handles(JNIHandleBlock::allocate_block());
   MACOS_AARCH64_ONLY(main_thread->init_wx());
 
+  MutexLockerImpl::init_counters(); // depends on mutex_init(), perfMemory_init(), and Thread::initialize_thread_current().
+
   // Set the lock_id now since we will run Java code before the Thread instance
   // is even created. The same value will be assigned to the Thread instance on init.
   main_thread->set_lock_id(ThreadIdentifier::next());
 
   if (!Thread::set_as_starting_thread(main_thread)) {
@@ -568,17 +636,23 @@
 
   // Initialize Java-Level synchronization subsystem
   ObjectMonitor::Initialize();
   ObjectSynchronizer::initialize();
 
+  Deoptimization::init_counters();
+  VMThread::init_counters();
+
   // Initialize global modules
   jint status = init_globals();
   if (status != JNI_OK) {
     main_thread->smr_delete();
     *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
     return status;
   }
+  if (xtty != nullptr)
+    xtty->elem("vm_main_thread thread='" UINTX_FORMAT "'",
+               (uintx) main_thread->osthread()->thread_id());
 
   // Create WatcherThread as soon as we can since we need it in case
   // of hangs during error reporting.
   WatcherThread::start();
 
@@ -719,37 +793,17 @@
   ServiceThread::initialize();
 
   // Start the monitor deflation thread:
   MonitorDeflationThread::initialize();
 
-  // initialize compiler(s)
-#if defined(COMPILER1) || COMPILER2_OR_JVMCI
-  bool init_compilation = true;
-#if INCLUDE_JVMCI
-  bool force_JVMCI_initialization = false;
-  if (EnableJVMCI) {
-    // Initialize JVMCI eagerly when it is explicitly requested.
-    // Or when JVMCILibDumpJNIConfig or JVMCIPrintProperties is enabled.
-    force_JVMCI_initialization = EagerJVMCI || JVMCIPrintProperties || JVMCILibDumpJNIConfig;
-    if (!force_JVMCI_initialization && UseJVMCICompiler && !UseJVMCINativeLibrary && (!UseInterpreter || !BackgroundCompilation)) {
-      // Force initialization of jarjvmci otherwise requests for blocking
-      // compilations will not actually block until jarjvmci is initialized.
-      force_JVMCI_initialization = true;
-    }
-    if (JVMCIPrintProperties || JVMCILibDumpJNIConfig) {
-      // Both JVMCILibDumpJNIConfig and JVMCIPrintProperties exit the VM
-      // so compilation should be disabled. This prevents dumping or
-      // printing from happening more than once.
-      init_compilation = false;
-    }
-  }
-#endif
-  if (init_compilation) {
-    CompileBroker::compilation_init(CHECK_JNI_ERR);
-  }
+#if INCLUDE_CDS
+  // Start the method sampler
+  MethodProfiler::initialize();
 #endif
 
+  bool force_JVMCI_initialization = initialize_compilation(CHECK_JNI_ERR);
+
   if (CDSConfig::is_using_aot_linked_classes()) {
     AOTLinkedClassBulkLoader::finish_loading_javabase_classes(CHECK_JNI_ERR);
     SystemDictionary::restore_archived_method_handle_intrinsics();
   }
 
@@ -766,11 +820,11 @@
 
   // This will initialize the module system.  Only java.base classes can be
   // loaded until phase 2 completes
   call_initPhase2(CHECK_JNI_ERR);
 
-  if (CDSConfig::is_using_aot_linked_classes()) {
+  if (CDSConfig::is_using_aot_linked_classes() && !CDSConfig::is_dumping_final_static_archive()) {
     AOTLinkedClassBulkLoader::load_non_javabase_classes(THREAD);
   }
 #ifndef PRODUCT
   HeapShared::initialize_test_class_from_archive(THREAD);
 #endif
@@ -788,10 +842,15 @@
   call_initPhase3(CHECK_JNI_ERR);
 
   // cache the system and platform class loaders
   SystemDictionary::compute_java_loaders(CHECK_JNI_ERR);
 
+  // Initiate replay training processing once preloading is over.
+  CompileBroker::init_training_replay();
+
+  AOTLinkedClassBulkLoader::replay_training_at_init_for_preloaded_classes(CHECK_JNI_ERR);
+
   if (Continuations::enabled()) {
     // Initialize Continuation class now so that failure to create enterSpecial/doYield
     // special nmethods due to limited CodeCache size can be treated as a fatal error at
     // startup with the proper message that CodeCache size is too small.
     initialize_class(vmSymbols::jdk_internal_vm_Continuation(), CHECK_JNI_ERR);
@@ -810,10 +869,24 @@
   if (force_JVMCI_initialization) {
     JVMCI::initialize_compiler(CHECK_JNI_ERR);
   }
 #endif
 
+  if (PrecompileCode) {
+    Precompiler::compile_cached_code(CHECK_JNI_ERR);
+    if (PrecompileOnlyAndExit) {
+      SCCache::close();
+      log_vm_init_stats();
+      vm_direct_exit(0, "Code precompiation is over");
+    }
+  }
+
+#if defined(COMPILER2)
+  // Pre-load cached compiled methods
+  SCCache::preload_code(CHECK_JNI_ERR);
+#endif
+
   if (NativeHeapTrimmer::enabled()) {
     NativeHeapTrimmer::initialize();
   }
 
   // Always call even when there are not JVMTI environments yet, since environments
@@ -858,18 +931,37 @@
   create_vm_timer.end();
 #ifdef ASSERT
   _vm_complete = true;
 #endif
 
-  if (CDSConfig::is_dumping_static_archive()) {
+  if (CDSConfig::is_dumping_classic_static_archive()) {
+    // Classic -Xshare:dump, aka "old workflow"
+    MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
+  } else if (CDSConfig::is_dumping_final_static_archive()) {
+    // TODO: copy the verification and loader constraints from preimage to final image
+    // TODO: load archived classes for custom loaders as well.
+    log_info(cds)("Dumping final image of CacheDataStore %s", CacheDataStore);
     MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
+    vm_direct_exit(0, "CacheDataStore dumping is complete");
   }
 
-  if (log_is_enabled(Info, perf, class, link)) {
-    LogStreamHandle(Info, perf, class, link) log;
-    log.print_cr("At VM initialization completion:");
-    ClassLoader::print_counters(&log);
+  log_info(init)("At VM initialization completion:");
+  log_vm_init_stats();
+
+  if (UsePerfData) {
+    if (ProfileVMLocks) {
+      main_thread->set_profile_vm_locks(true);
+    }
+    if (ProfileVMCalls) {
+      main_thread->set_profile_vm_calls(true);
+    }
+    if (ProfileRuntimeCalls) {
+      main_thread->set_profile_rt_calls(true);
+    }
+    if (ProfileVMOps) {
+      main_thread->set_profile_vm_ops(true);
+    }
   }
 
   return JNI_OK;
 }
 
diff a/src/hotspot/share/runtime/threads.hpp b/src/hotspot/share/runtime/threads.hpp
--- a/src/hotspot/share/runtime/threads.hpp
+++ b/src/hotspot/share/runtime/threads.hpp
@@ -52,10 +52,11 @@
 #ifdef ASSERT
   static bool        _vm_complete;
 #endif
 
   static void initialize_java_lang_classes(JavaThread* main_thread, TRAPS);
+  static bool initialize_compilation(TRAPS);
   static void initialize_jsr292_core_classes(TRAPS);
 
  public:
   // Thread management
   // force_daemon is a concession to JNI, where we may need to add a
diff a/src/hotspot/share/runtime/timer.cpp b/src/hotspot/share/runtime/timer.cpp
--- a/src/hotspot/share/runtime/timer.cpp
+++ b/src/hotspot/share/runtime/timer.cpp
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #include "logging/log.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/os.hpp"
+#include "runtime/thread.hpp"
 #include "runtime/timer.hpp"
 #include "utilities/ostream.hpp"
 
 double TimeHelper::counter_to_seconds(jlong counter) {
   double freq  = (double) os::elapsed_frequency();
@@ -46,49 +47,78 @@
 jlong TimeHelper::micros_to_counter(jlong micros) {
   jlong freq = os::elapsed_frequency() / MICROUNITS;
   return micros * freq;
 }
 
-void elapsedTimer::add(elapsedTimer t) {
-  _counter += t._counter;
+void BaseTimer::add(BaseTimer* t) {
+  _counter += t->_counter;
 }
 
-void elapsedTimer::add_nanoseconds(jlong ns) {
+void BaseTimer::add_nanoseconds(jlong ns) {
   jlong freq = os::elapsed_frequency() / NANOUNITS;
   _counter += ns * freq;
 }
 
-void elapsedTimer::start() {
+void BaseTimer::start() {
   if (!_active) {
     _active = true;
-    _start_counter = os::elapsed_counter();
+    _start_counter = read_counter();
   }
 }
 
-void elapsedTimer::stop() {
+void BaseTimer::stop() {
   if (_active) {
-    _counter += os::elapsed_counter() - _start_counter;
+    _counter += read_counter() - _start_counter;
     _active = false;
   }
 }
 
-double elapsedTimer::seconds() const {
+double BaseTimer::seconds() const {
  return TimeHelper::counter_to_seconds(_counter);
 }
 
-jlong elapsedTimer::milliseconds() const {
+jlong BaseTimer::milliseconds() const {
   return (jlong)TimeHelper::counter_to_millis(_counter);
 }
 
-jlong elapsedTimer::active_ticks() const {
+jlong BaseTimer::active_ticks() const {
   if (!_active) {
     return ticks();
   }
-  jlong counter = _counter + os::elapsed_counter() - _start_counter;
+  jlong counter = _counter + read_counter() - _start_counter;
   return counter;
 }
 
+jlong elapsedTimer::read_counter() const {
+  return os::elapsed_counter();
+}
+
+ThreadTimer::ThreadTimer() {
+  _owner = Thread::current();
+}
+
+void ThreadTimer::start() {
+  assert(_owner != nullptr, "timer must be bound to a thread");
+  Thread* current = Thread::current();
+  assert(current == _owner, "timer can only be started by the thread that owns it");
+  BaseTimer::start();
+}
+
+void ThreadTimer::stop() {
+  assert(_owner != nullptr, "sanity check");
+  Thread* current = Thread::current();
+  assert(current == _owner, "timer can only be stopped by the thread that owns it");
+  if (_start_counter != -1) {
+    BaseTimer::stop();
+  }
+}
+
+jlong ThreadTimer::read_counter() const {
+  assert(_owner != nullptr, "sanity check");
+  return os::thread_cpu_time(_owner);
+}
+
 void TimeStamp::update_to(jlong ticks) {
   _counter = ticks;
   if (_counter == 0)  _counter = 1;
   assert(is_updated(), "must not look clear");
 }
diff a/src/hotspot/share/runtime/timer.hpp b/src/hotspot/share/runtime/timer.hpp
--- a/src/hotspot/share/runtime/timer.hpp
+++ b/src/hotspot/share/runtime/timer.hpp
@@ -27,30 +27,53 @@
 
 #include "utilities/globalDefinitions.hpp"
 
 // Timers for simple measurement.
 
-class elapsedTimer {
+class BaseTimer {
   friend class VMStructs;
- private:
+ protected:
   jlong _counter;
   jlong _start_counter;
   bool  _active;
+
+  virtual jlong read_counter() const = 0;
+
  public:
-  elapsedTimer()             { _active = false; reset(); }
-  void add(elapsedTimer t);
+  BaseTimer() {
+    _active = false;
+    reset();
+  }
+  void add(BaseTimer* t);
   void add_nanoseconds(jlong ns);
-  void start();
-  void stop();
+  virtual void start();
+  virtual void stop();
   void reset()               { _counter = 0; }
   double seconds() const;
   jlong milliseconds() const;
   jlong ticks() const        { return _counter; }
   jlong active_ticks() const;
   bool  is_active() const { return _active; }
 };
 
+class elapsedTimer: public BaseTimer {
+  friend class VMStructs;
+ public:
+  jlong read_counter() const override;
+};
+
+class ThreadTimer: public BaseTimer {
+ private:
+  Thread* _owner;
+ public:
+  ThreadTimer();
+  ThreadTimer(Thread* thread) : _owner(thread) {}
+  void start() override;
+  void stop() override;
+  jlong read_counter() const override;
+};
+
 // TimeStamp is used for recording when an event took place.
 class TimeStamp {
  private:
   jlong _counter;
  public:
diff a/src/hotspot/share/runtime/timerTrace.cpp b/src/hotspot/share/runtime/timerTrace.cpp
--- a/src/hotspot/share/runtime/timerTrace.cpp
+++ b/src/hotspot/share/runtime/timerTrace.cpp
@@ -71,11 +71,11 @@
   if (!_active) {
     return;
   }
   _t.stop();
   if (_accum != nullptr) {
-    _accum->add(_t);
+    _accum->add(&_t);
   }
   if (!_verbose) {
     return;
   }
   if (_print) {
diff a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -1253,10 +1253,11 @@
         declare_type(JvmtiAgentThread, JavaThread)                        \
         declare_type(MonitorDeflationThread, JavaThread)                  \
         declare_type(ServiceThread, JavaThread)                           \
         declare_type(NotificationThread, JavaThread)                      \
         declare_type(CompilerThread, JavaThread)                          \
+        declare_type(TrainingReplayThread, JavaThread)                    \
         declare_type(StringDedupThread, JavaThread)                       \
         declare_type(AttachListenerThread, JavaThread)                    \
   declare_toplevel_type(OSThread)                                         \
   declare_toplevel_type(JavaFrameAnchor)                                  \
                                                                           \
diff a/src/hotspot/share/runtime/vmThread.cpp b/src/hotspot/share/runtime/vmThread.cpp
--- a/src/hotspot/share/runtime/vmThread.cpp
+++ b/src/hotspot/share/runtime/vmThread.cpp
@@ -36,20 +36,23 @@
 #include "oops/verifyOopClosure.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/cpuTimeCounters.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/java.hpp"
 #include "runtime/javaThread.inline.hpp"
 #include "runtime/jniHandles.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/os.hpp"
-#include "runtime/perfData.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/synchronizer.hpp"
 #include "runtime/timerTrace.hpp"
 #include "runtime/vmThread.hpp"
+#include "runtime/vmOperation.hpp"
 #include "runtime/vmOperations.hpp"
+#include "services/management.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/events.hpp"
 #include "utilities/vmError.hpp"
 
 
@@ -272,11 +275,11 @@
 
 void VMThread::evaluate_operation(VM_Operation* op) {
   ResourceMark rm;
 
   {
-    PerfTraceTime vm_op_timer(perf_accumulated_vm_operation_time());
+    PerfTraceElapsedTime vm_op_timer(perf_accumulated_vm_operation_time());
     HOTSPOT_VMOPS_BEGIN(
                      (char *) op->name(), strlen(op->name()),
                      op->evaluate_at_safepoint() ? 0 : 1);
 
     EventExecuteVMOperation event;
@@ -520,10 +523,13 @@
 };
 
 void VMThread::execute(VM_Operation* op) {
   Thread* t = Thread::current();
 
+  PerfTraceTimedEvent p(VMThread::get_perf_timer_for(op), VMThread::get_perf_counter_for(op), \
+                        Thread::current()->profile_vm_ops()); \
+
   if (t->is_VM_thread()) {
     op->set_calling_thread(t);
     ((VMThread*)t)->inner_execute(op);
     return;
   }
@@ -549,5 +555,89 @@
 }
 
 void VMThread::verify() {
   oops_do(&VerifyOopClosure::verify_oop, nullptr);
 }
+
+#define DECLARE_COUNTER(name) \
+PerfTickCounters* _perf_##name##_timer = nullptr; \
+PerfCounter*      _perf_##name##_count = nullptr;
+
+VM_OPS_DO(DECLARE_COUNTER)
+
+#undef DECLARE_COUNTER
+
+#define SWITCH_TIMER(name) \
+case VM_Operation::VMOp_##name: return _perf_##name##_timer;
+
+PerfTickCounters* VMThread::get_perf_timer_for(VM_Operation *op) {
+  switch(op->type()) {
+    VM_OPS_DO(SWITCH_TIMER)
+    default: ShouldNotReachHere();
+  }
+}
+
+#undef SWITCH_TIMER
+
+#define SWITCH_COUNT(name) \
+case VM_Operation::VMOp_##name: return _perf_##name##_count;
+
+PerfCounter* VMThread::get_perf_counter_for(VM_Operation *op) {
+  switch(op->type()) {
+    VM_OPS_DO(SWITCH_COUNT)
+    default: ShouldNotReachHere();
+  }
+}
+
+#undef SWITCH_COUNT
+
+#define INIT_COUNTER(name) \
+    NEWPERFTICKCOUNTERS(_perf_##name##_timer, SUN_RT, #name "_time"); \
+    NEWPERFEVENTCOUNTER(_perf_##name##_count, SUN_RT, #name "_count"); \
+
+void VMThread::init_counters() {
+  if (ProfileVMOps && UsePerfData) {
+    EXCEPTION_MARK;
+
+    VM_OPS_DO(INIT_COUNTER)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("jvm_perf_init failed unexpectedly");
+    }
+  }
+}
+
+#undef INIT_COUNTER
+
+static jlong total_count() {
+  jlong total = 0;
+#define ACC_COUNT(name) total += _perf_##name##_count->get_value();
+  VM_OPS_DO(ACC_COUNT)
+#undef ACC_COUNT
+  return total;
+}
+
+static jlong total_elapsed_time_in_ms() {
+  jlong total_elapsed_ticks = 0;
+#define ACC_COUNT(name) total_elapsed_ticks += _perf_##name##_timer->elapsed_counter_value();
+  VM_OPS_DO(ACC_COUNT)
+#undef ACC_COUNT
+  return Management::ticks_to_ms(total_elapsed_ticks);
+}
+
+#define PRINT_COUNTER(name) {\
+  jlong count = _perf_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-40s = " JLONG_FORMAT_W(6) "us (" JLONG_FORMAT_W(5) " events)", #name, _perf_##name##_timer->elapsed_counter_value_us(), count); \
+  }}
+
+void VMThread::print_counters_on(outputStream* st) {
+  if (ProfileVMOps && UsePerfData) {
+    st->print_cr("VMOperation: Total: " JLONG_FORMAT " events (elapsed " JLONG_FORMAT "ms) for thread \"main\":",
+                 total_count(), total_elapsed_time_in_ms());
+    VM_OPS_DO(PRINT_COUNTER)
+  } else {
+    st->print_cr("  VMOperations:  no info (%s is disabled)", (UsePerfData ? "ProfileVMCalls" : "UsePerfData"));
+  }
+}
+
+#undef PRINT_COUNTER
diff a/src/hotspot/share/runtime/vmThread.hpp b/src/hotspot/share/runtime/vmThread.hpp
--- a/src/hotspot/share/runtime/vmThread.hpp
+++ b/src/hotspot/share/runtime/vmThread.hpp
@@ -133,17 +133,23 @@
   static void wait_until_executed(VM_Operation* op);
 
   // Printing
   const char* type_name() const { return "VMThread"; }
 
+  static void init_counters();
+  static void print_counters_on(outputStream* st);
+
  private:
   // VM_Operation support
   static VM_Operation*     _cur_vm_operation;   // Current VM operation
   static VM_Operation*     _next_vm_operation;  // Next VM operation
 
   bool set_next_operation(VM_Operation *op);    // Set the _next_vm_operation if possible.
 
   // Pointer to single-instance of VM thread
   static VMThread*     _vm_thread;
+
+  static PerfTickCounters* get_perf_timer_for(VM_Operation* op);
+  static PerfCounter* get_perf_counter_for(VM_Operation* op);
 };
 
 #endif // SHARE_RUNTIME_VMTHREAD_HPP
diff a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp
--- a/src/hotspot/share/services/diagnosticCommand.cpp
+++ b/src/hotspot/share/services/diagnosticCommand.cpp
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "cds/cdsConfig.hpp"
+#include "cds/cdsEndTrainingUpcall.hpp"
 #include "cds/cds_globals.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/classLoaderHierarchyDCmd.hpp"
 #include "classfile/classLoaderStats.hpp"
 #include "classfile/javaClasses.hpp"
@@ -131,10 +132,13 @@
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderStatsDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderHierarchyDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompileQueueDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeListDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
+#if INCLUDE_CDS
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<AOTEndTrainingDCmd>(full_export, true, false));
+#endif // INCLUDE_CDS
 #ifdef LINUX
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PerfMapDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<MallocInfoDcmd>(full_export, true, false));
 #endif // LINUX
@@ -984,10 +988,22 @@
 void ClassesDCmd::execute(DCmdSource source, TRAPS) {
   VM_PrintClasses vmop(output(), _verbose.value());
   VMThread::execute(&vmop);
 }
 
+#if INCLUDE_CDS
+void AOTEndTrainingDCmd::execute(DCmdSource source, TRAPS) {
+  if (!CDSConfig::is_dumping_preimage_static_archive()) {
+    output()->print_cr("Error! Not a training run");
+  } else if (CDSEndTrainingUpcall::end_training(THREAD)) {
+    output()->print_cr("Training ended successfully");
+  } else {
+    output()->print_cr("Error! Failed to end training");
+  }
+}
+#endif // INCLUDE_CDS
+
 #if INCLUDE_CDS
 #define DEFAULT_CDS_ARCHIVE_FILENAME "java_pid%p_<subcmd>.jsa"
 
 DumpSharedArchiveDCmd::DumpSharedArchiveDCmd(outputStream* output, bool heap) :
                                      DCmdWithParser(output, heap),
diff a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp
--- a/src/hotspot/share/services/diagnosticCommand.hpp
+++ b/src/hotspot/share/services/diagnosticCommand.hpp
@@ -393,10 +393,30 @@
     return p;
   }
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+#if INCLUDE_CDS
+class AOTEndTrainingDCmd : public DCmd {
+public:
+  AOTEndTrainingDCmd(outputStream* output, bool heap) : DCmd(output, heap) { }
+    static const char* name() { return "AOT.end_training"; }
+    static const char* description() {
+      return "End AOT training and create the cache.";
+    }
+    static const char* impact() {
+      return "Medium: Pause time depends on number of loaded classes";
+    }
+    static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "monitor", nullptr};
+      return p;
+    }
+    virtual void execute(DCmdSource source, TRAPS);
+};
+#endif // INCLUDE_CDS
+
 #if INCLUDE_CDS
 class DumpSharedArchiveDCmd: public DCmdWithParser {
 protected:
   DCmdArgument<char*> _suboption;   // option of VM.cds
   DCmdArgument<char*> _filename;    // file name, optional
diff a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp
--- a/src/hotspot/share/services/management.cpp
+++ b/src/hotspot/share/services/management.cpp
@@ -2092,10 +2092,16 @@
   assert(os::elapsed_frequency() > 0, "Must be non-zero");
   return (jlong)(((double)ticks / (double)os::elapsed_frequency())
                  * (double)1000.0);
 }
 
+jlong Management::ticks_to_us(jlong ticks) {
+  assert(os::elapsed_frequency() > 0, "Must be non-zero");
+  return (jlong)(((double)ticks / (double)os::elapsed_frequency())
+                 * (double)1000000.0);
+}
+
 // Gets the amount of memory allocated on the Java heap since JVM launch.
 JVM_ENTRY(jlong, jmm_GetTotalThreadAllocatedMemory(JNIEnv *env))
     // A thread increments exited_allocated_bytes in ThreadService::remove_thread
     // only after it removes itself from the threads list, and once a TLH is
     // created, no thread it references can remove itself from the threads
diff a/src/hotspot/share/services/management.hpp b/src/hotspot/share/services/management.hpp
--- a/src/hotspot/share/services/management.hpp
+++ b/src/hotspot/share/services/management.hpp
@@ -61,10 +61,11 @@
 public:
   static void init();
   static void initialize(TRAPS);
 
   static jlong ticks_to_ms(jlong ticks) NOT_MANAGEMENT_RETURN_(0L);
+  static jlong ticks_to_us(jlong ticks) NOT_MANAGEMENT_RETURN_(0L);
   static jlong timestamp() NOT_MANAGEMENT_RETURN_(0L);
 
   static void* get_jmm_interface(int version);
   static void  get_optional_support(jmmOptionalSupport* support);
 
diff a/src/hotspot/share/utilities/checkedCast.hpp b/src/hotspot/share/utilities/checkedCast.hpp
--- a/src/hotspot/share/utilities/checkedCast.hpp
+++ b/src/hotspot/share/utilities/checkedCast.hpp
@@ -24,10 +24,11 @@
 
 #ifndef SHARE_UTILITIES_CHECKEDCAST_HPP
 #define SHARE_UTILITIES_CHECKEDCAST_HPP
 
 #include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
 
 // In many places we've added C-style casts to silence compiler
 // warnings, for example when truncating a size_t to an int when we
 // know the size_t is a small struct. Such casts are risky because
 // they effectively disable useful compiler warnings. We can make our
@@ -36,11 +37,11 @@
 // everything: it isn't intended to make sure that pointer types are
 // compatible, for example.
 template <typename T2, typename T1>
 constexpr T2 checked_cast(T1 thing) {
   T2 result = static_cast<T2>(thing);
-  assert(static_cast<T1>(result) == thing, "must be");
+  assert(static_cast<T1>(result) == thing, "must be, thing: " INTPTR_FORMAT ", result: " INTPTR_FORMAT, (intptr_t)thing, (intptr_t)result);
   return result;
 }
 
 #endif // SHARE_UTILITIES_CHECKEDCAST_HPP
 
diff a/src/hotspot/share/utilities/classpathStream.hpp b/src/hotspot/share/utilities/classpathStream.hpp
--- a/src/hotspot/share/utilities/classpathStream.hpp
+++ b/src/hotspot/share/utilities/classpathStream.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2024, 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.
@@ -42,8 +42,17 @@
   bool has_next() {
     return _start < _len;
   }
 
   const char* get_next();
+
+  // Number of characters have been consumed from the input (this->_classpath)
+  // ClasspathStream cps("a:b:c");
+  // cps.next();    /* returns "a" */
+  // cps.next();    /* returns "b" */
+  // cps.num_chars_consumed() == 3;
+  size_t num_chars_consumed() {
+    return (size_t)_start;
+  }
 };
 
 #endif // SHARE_UTILITIES_CLASSPATHSTREAM_HPP
diff a/src/hotspot/share/utilities/constantTag.cpp b/src/hotspot/share/utilities/constantTag.cpp
--- a/src/hotspot/share/utilities/constantTag.cpp
+++ b/src/hotspot/share/utilities/constantTag.cpp
@@ -147,9 +147,8 @@
     case JVM_CONSTANT_ClassIndex :
       return "Unresolved Class Index";
     case JVM_CONSTANT_StringIndex :
       return "Unresolved String Index";
     default:
-      ShouldNotReachHere();
       return "Illegal";
   }
 }
diff a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp
--- a/src/hotspot/share/utilities/ostream.cpp
+++ b/src/hotspot/share/utilities/ostream.cpp
@@ -682,10 +682,14 @@
   FREE_C_HEAP_ARRAY(char, try_name);
   if (file->is_open()) {
     return file;
   }
 
+  if (RecordTraining) {
+    vm_exit_during_initialization("cannot create log file for RecordTraining mode: %s", try_name);
+  }
+
   // Try again to open the file in the temp directory.
   delete file;
   // Note: This feature is for maintainer use only.  No need for L10N.
   jio_printf("Warning:  Cannot open log file: %s\n", log_name);
   try_name = make_log_name(log_name, os::get_temp_directory());
@@ -737,42 +741,45 @@
              " process='%d' time_ms='" INT64_FORMAT "'",
              LOG_MAJOR_VERSION, LOG_MINOR_VERSION,
              os::current_process_id(), (int64_t)time_ms);
     // Write VM version header immediately.
     xs->head("vm_version");
-    xs->head("name"); xs->text("%s", VM_Version::vm_name()); xs->cr();
+    xs->head("name");  // FIXME: should be an elem attr, not head/tail pair
+    xs->log_only()->print_cr("%s", VM_Version::vm_name());
     xs->tail("name");
-    xs->head("release"); xs->text("%s", VM_Version::vm_release()); xs->cr();
+    xs->head("release");
+    xs->log_only()->print_cr("%s", VM_Version::vm_release());
     xs->tail("release");
-    xs->head("info"); xs->text("%s", VM_Version::internal_vm_info_string()); xs->cr();
+    xs->head("info");
+    xs->log_only()->print_cr("%s", VM_Version::internal_vm_info_string());
     xs->tail("info");
     xs->tail("vm_version");
     // Record information about the command-line invocation.
     xs->head("vm_arguments");  // Cf. Arguments::print_on()
     if (Arguments::num_jvm_flags() > 0) {
       xs->head("flags");
-      Arguments::print_jvm_flags_on(xs->text());
+      Arguments::print_jvm_flags_on(xs->log_only());
       xs->tail("flags");
     }
     if (Arguments::num_jvm_args() > 0) {
       xs->head("args");
-      Arguments::print_jvm_args_on(xs->text());
+      Arguments::print_jvm_args_on(xs->log_only());
       xs->tail("args");
     }
     if (Arguments::java_command() != nullptr) {
-      xs->head("command"); xs->text()->print_cr("%s", Arguments::java_command());
+      xs->head("command"); xs->log_only()->print_cr("%s", Arguments::java_command());
       xs->tail("command");
     }
     if (Arguments::sun_java_launcher() != nullptr) {
-      xs->head("launcher"); xs->text()->print_cr("%s", Arguments::sun_java_launcher());
+      xs->head("launcher"); xs->log_only()->print_cr("%s", Arguments::sun_java_launcher());
       xs->tail("launcher");
     }
     if (Arguments::system_properties() !=  nullptr) {
       xs->head("properties");
       // Print it as a java-style property list.
       // System properties don't generally contain newlines, so don't bother with unparsing.
-      outputStream *text = xs->text();
+      outputStream *text = xs->log_only();
       for (SystemProperty* p = Arguments::system_properties(); p != nullptr; p = p->next()) {
         assert(p->key() != nullptr, "p->key() is null");
         if (p->readable()) {
           // Print in two stages to avoid problems with long
           // keys/values.
@@ -787,10 +794,11 @@
     xs->tail("vm_arguments");
     // tty output per se is grouped under the <tty>...</tty> element.
     xs->head("tty");
     // All further non-markup text gets copied to the tty:
     xs->_text = this;  // requires friend declaration!
+    // (And xtty->log_only() remains as a back door to the non-tty log file.)
 }
 
 // finish_log() is called during normal VM shutdown. finish_log_on_error() is
 // called by ostream_abort() after a fatal error.
 //
diff a/src/hotspot/share/utilities/xmlstream.cpp b/src/hotspot/share/utilities/xmlstream.cpp
--- a/src/hotspot/share/utilities/xmlstream.cpp
+++ b/src/hotspot/share/utilities/xmlstream.cpp
@@ -30,25 +30,71 @@
 #include "oops/methodData.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/osThread.hpp"
 #include "runtime/vmOperations.hpp"
 #include "runtime/vmThread.hpp"
 #include "utilities/vmError.hpp"
 #include "utilities/xmlstream.hpp"
 
+// The XML stream is the contents of the LogFile (default hotspot_%p.log).
+// It is a superset of whatever might be displayed on the tty.
+// You can get to it by calls of the form xtty->...
+// Normal calls to tty->... just embed plain text among any markup
+// produced via the xtty API.
+// The xtty has sub-streams called xtty->text() and xtty->log_long().
+// These are ordinary output streams for writing unstructured text.
+// The format of this log file is both unstructured and constrained.
+//
+// Apart from possible race conditions, every line in the log file
+// is either an XML element (<tag ...>, or </tag>, or <tag .../>)
+// or is unstructured text.
+//
+// On any given line, if the first character is '<', then the last
+// character is '>' and the line consists of a single XML element,
+// which uses single quote '\'' to delimit any attribute values.
+// (The double-quote character '"' never appears, ever.)
+//
+// All other lines consist of unstructured text which is completely
+// free of the following characters: '<', '>', '&', '\'', '"'.  If
+// those characters are written to the tty (or to any other text
+// stream underlying the xtty), those characters, and no other
+// characters, are written as XML entities: "&lt;", "&gt;", "&amp;",
+// "&apos", "&quot".  There is no other use of the character '&'.
+//
+// The net effect is that you may select a range of tools to process
+// the marked-up logs, including XML parsers and simple line-oriented
+// Java or Unix tools.  The main concession you have to make to XML
+// is to convert the above five XML entities to single ASCII chars,
+// as you process attribute strings or unstructured text.
+//
+// It would be wise to ignore any XML tags that you do not recognize.
+// This can be done with grep, if you choose, because the log file
+// is line-structured.
+//
+// The log file collects the output from many contributing threads.
+// You should expect that an element of the form <writer thread='NNN'>
+// could appear almost anywhere, as the lines interleave.
+// It is straightforward to write a script to tease the log file
+// into thread-specific substreams.
+
 // Do not assert this condition if there's already another error reported.
 #define assert_if_no_error(cond, msg) \
   vmassert((cond) || VMError::is_error_reported(), msg)
+bool xmlStream::inside_attrs_or_error() {
+  return inside_attrs() || VMError::is_error_reported();
+}
 
 void xmlStream::initialize(outputStream* out) {
   _out = out;
   _last_flush = 0;
   _markup_state = BODY;
   _text_init._outer_xmlStream = this;
   _text = &_text_init;
+  _log_only = _text;
 
 #ifdef ASSERT
   _element_depth = 0;
   int   init_len = 100;
   char* init_buf = NEW_C_HEAP_ARRAY(char, init_len, mtInternal);
@@ -127,19 +173,30 @@
   va_start(ap, format);
   va_text(format, ap);
   va_end(ap);
 }
 
+// ------------------------------------------------------------------
+// Outputs XML attribute, with quotes and special characters quoted.
+void xmlStream::attr(const char* attr, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  va_attr(attr, format, ap);
+  va_end(ap);
+}
+
 #define BUFLEN 2*K   /* max size of output of individual print methods */
 
 // ------------------------------------------------------------------
 void xmlStream::va_tag(bool push, const char* format, va_list ap) {
   assert_if_no_error(!inside_attrs(), "cannot print tag inside attrs");
   char buffer[BUFLEN];
   size_t len;
   const char* kind = do_vsnprintf(buffer, BUFLEN, format, ap, false, len);
   see_tag(kind, push);
+  // make sure all opening and/or closing tags begin in the first column
+  if (out()->position() > 0)  print_raw("\n");
   print_raw("<");
   write(kind, len);
   _markup_state = (push ? HEAD : ELEM);
 }
 
@@ -313,10 +370,12 @@
 
 // ------------------------------------------------------------------
 // Outputs "</kind>".
 void xmlStream::tail(const char* kind) {
   pop_tag(kind);
+  // make sure all opening and/or closing tags begin in the first column
+  if (out()->position() > 0)  print_raw("\n");
   print_raw("</");
   print_raw(kind);
   print_raw(">\n");
 }
 
@@ -374,27 +433,39 @@
 }
 PRAGMA_DIAG_POP
 
 // Output a timestamp attribute.
 void xmlStream::stamp() {
-  assert_if_no_error(inside_attrs(), "stamp must be an attribute");
+  assert(inside_attrs_or_error(), "stamp must be an attribute");
   print_raw(" stamp='");
   out()->stamp();
   print_raw("'");
 }
 
 
 // ------------------------------------------------------------------
 // Output a method attribute, in the form " method='pkg/cls name sig'".
 // This is used only when there is no ciMethod available.
-void xmlStream::method(Method* method) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+void xmlStream::method(Method* method, const char* pfx) {
+  assert(inside_attrs_or_error(), "printing attributes");
   if (method == nullptr)  return;
-  print_raw(" method='");
+  if (*pfx) {
+    print(" %smethod='", pfx);
+    method_text(method);
+    print_raw("'");
+    return;
+  }
+  print(" method='");
   method_text(method);
   print("' bytes='%d'", method->code_size());
   print(" count='%d'", method->invocation_count());
+  if (RecordTraining) {
+    // print stuff about this method's compilation history
+    print(" highest_comp_level='%d'", method->highest_comp_level());
+    nmethod* nm = method->code();
+    if (nm != nullptr)  print(" last_compile_id='%d'", nm->compile_id());
+  }
   int bec = method->backedge_count();
   if (bec != 0)  print(" backedge_count='%d'", bec);
   print(" iicount='%d'", method->interpreter_invocation_count());
   int throwouts = method->interpreter_throwout_count();
   if (throwouts != 0)  print(" throwouts='%d'", throwouts);
@@ -414,11 +485,11 @@
   }
 }
 
 void xmlStream::method_text(Method* method) {
   ResourceMark rm;
-  assert_if_no_error(inside_attrs(), "printing attributes");
+  assert(inside_attrs_or_error(), "printing attributes");
   if (method == nullptr)  return;
   text()->print("%s", method->method_holder()->external_name());
   print_raw(" ");  // " " is easier for tools to parse than "::"
   method->name()->print_symbol_on(text());
   print_raw(" ");  // separator
@@ -427,69 +498,131 @@
 
 
 // ------------------------------------------------------------------
 // Output a klass attribute, in the form " klass='pkg/cls'".
 // This is used only when there is no ciKlass available.
-void xmlStream::klass(Klass* klass) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+void xmlStream::klass(Klass* klass, const char* pfx) {
+  assert(inside_attrs_or_error(), "printing attributes");
   if (klass == nullptr) return;
-  print_raw(" klass='");
+  print(" %sklass='", pfx);
   klass_text(klass);
   print_raw("'");
+  if (klass->class_loader() != nullptr) {
+    loader(klass->class_loader(), pfx);
+  }
 }
 
 void xmlStream::klass_text(Klass* klass) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+  assert(inside_attrs_or_error(), "printing attributes");
   if (klass == nullptr) return;
   //klass->print_short_name(log->out());
   klass->name()->print_symbol_on(out());
+  if (klass->is_hidden()) {
+    out()->print(" //hidden");
+    // FIXME:  maybe hash the contents of its classfile
+  }
 }
 
-void xmlStream::name(const Symbol* name) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+void xmlStream::loader(oop cl, const char* pfx) {
+  assert(inside_attrs_or_error(), "printing attributes");
+  if (cl == nullptr) return;
+  print(" %sloader='", pfx);
+  loader_text(cl);
+  print_raw("'");
+}
+
+void xmlStream::loader_text(oop cl) {
+  assert(inside_attrs_or_error(), "printing attributes");
+  if (cl == nullptr) return;
+  oop id = java_lang_ClassLoader::nameAndId(cl);
+  if (id != nullptr)  string_text(id);
+}
+
+void xmlStream::name(const Symbol* name, const char* pfx) {
+  assert(inside_attrs_or_error(), "printing attributes");
   if (name == nullptr)  return;
-  print_raw(" name='");
-  name_text(name);
+  print(" %sname='", pfx);
+  symbol_text(name);
   print_raw("'");
 }
 
-void xmlStream::name_text(const Symbol* name) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+void xmlStream::signature(const Symbol* sig, const char* pfx) {
+  assert(inside_attrs_or_error(), "printing attributes");
+  if (sig == nullptr)  return;
+  print(" %ssignature='", pfx);
+  symbol_text(sig);
+  print_raw("'");
+}
+
+void xmlStream::symbol_text(const Symbol* name) {
+  assert(inside_attrs_or_error(), "printing attributes");
   if (name == nullptr)  return;
   //name->print_short_name(text());
-  name->print_symbol_on(text());
+  //name->print_symbol_on(text());  // this has odd escapes in it (\\x%04x)
+  const char* base = (const char*) name->base();
+  int len = name->utf8_length();
+  log_only()->write(base, len);
+}
+
+void xmlStream::string_text(oop str) {
+  assert(inside_attrs_or_error(), "printing attributes");
+  if (str == nullptr)  return;
+  if (!java_lang_String::is_instance(str)) {
+    print("*** not a string*** ");
+    str->print_value_on(log_only());
+    return;
+  }
+  ResourceMark rm;
+  log_only()->print_raw(java_lang_String::as_utf8_string(str));
+}
+
+void xmlStream::thread(Thread* t, const char* pfx) {
+  assert(inside_attrs_or_error(), "printing attributes");
+  intx tid = t == nullptr ? os::current_thread_id() : t->osthread()->thread_id();
+  guarantee(tid == (t == nullptr ? Thread::current() : t)->osthread()->thread_id(), "");
+  print(" %sthread='" INTX_FORMAT "'", pfx, tid);
 }
 
 void xmlStream::object(const char* attr, Handle x) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+  assert(inside_attrs_or_error(), "printing attributes");
   if (x == nullptr)  return;
   print_raw(" ");
   print_raw(attr);
   print_raw("='");
   object_text(x);
   print_raw("'");
 }
 
 void xmlStream::object_text(Handle x) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
-  if (x == nullptr)  return;
-  x->print_value_on(text());
+  assert(inside_attrs_or_error(), "printing attributes");
+  if (x.is_null())  return;
+  if (java_lang_ClassLoader::is_instance(x())) {
+    print_raw("loader:");
+    loader_text(x());
+    return;
+  }
+  if (java_lang_String::is_instance(x())) {
+    print_raw("string:");
+    string_text(x());
+    return;
+  }
+  x->print_value_on(log_only());
 }
 
 
 void xmlStream::object(const char* attr, Metadata* x) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+  assert(inside_attrs_or_error(), "printing attributes");
   if (x == nullptr)  return;
   print_raw(" ");
   print_raw(attr);
   print_raw("='");
   object_text(x);
   print_raw("'");
 }
 
 void xmlStream::object_text(Metadata* x) {
-  assert_if_no_error(inside_attrs(), "printing attributes");
+  assert(inside_attrs_or_error(), "printing attributes");
   if (x == nullptr)  return;
   //x->print_value_on(text());
   if (x->is_method())
     method_text((Method*)x);
   else if (x->is_klass())
diff a/src/hotspot/share/utilities/xmlstream.hpp b/src/hotspot/share/utilities/xmlstream.hpp
--- a/src/hotspot/share/utilities/xmlstream.hpp
+++ b/src/hotspot/share/utilities/xmlstream.hpp
@@ -68,10 +68,11 @@
  protected:
   outputStream* _out;            // file stream by which it goes
   julong        _last_flush;     // last position of flush
   MarkupState   _markup_state;   // where in the elem/head/tail dance
   outputStream* _text;           // text stream
+  outputStream* _log_only;       // like text, except does not copy to the tty
   xmlTextStream _text_init;
 
   // for subclasses
   xmlStream() {}
   void initialize(outputStream* out);
@@ -99,10 +100,11 @@
 
   bool is_open() { return _out != nullptr; }
 
   // text output
   bool inside_attrs() { return _markup_state != BODY; }
+  bool inside_attrs_or_error();
 
   // flushing
   virtual void flush();  // flushes out, sets _last_flush = count()
   virtual void write(const char* s, size_t len);
   void    write_text(const char* s, size_t len);  // used by xmlTextStream
@@ -133,24 +135,45 @@
   void          text(const char* format, ...) ATTRIBUTE_PRINTF(2, 3);
   void       va_text(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0) {
     text()->vprint(format, ap);
   }
 
+  // write attr with formatted contents
+  void          attr(const char* attr, const char* format, ...) ATTRIBUTE_PRINTF(3, 4);
+  void       va_attr(const char* attr, const char* format, va_list ap) ATTRIBUTE_PRINTF(3, 0) {
+    print_raw(" ");
+    print_raw(attr);
+    print_raw("='");
+    va_text(format, ap);
+    print_raw("='");
+  }
+
+  outputStream* log_only() { return _log_only; }
+  // report if a given random stream is really part of this XML stream
+  bool owns(outputStream* st) {
+    return st == this || st == _text || st == _log_only || st == _out;
+  }
+
   // commonly used XML attributes
   void          stamp();                 // stamp='1.234'
-  void          method(Method* m);       // method='k n s' ...
-  void          klass(Klass* k);         // klass='name'
-  void          name(const Symbol* s);   // name='name'
+  void          method(Method* m, const char* pfx=""); // method='k n s' ...
+  void          klass(Klass* k, const char* pfx="");   // klass='name'
+  void          loader(oop cl, const char* pfx="");   // loader='...'
+  void          name(const Symbol* s, const char* pfx="");   // name='n'
+  void          signature(const Symbol* s, const char* pfx="");   // signature='...'
   void          object(const char* attr, Metadata* val);
   void          object(const char* attr, Handle val);
+  void          thread(Thread* t = nullptr, const char* pfx=""); // thread='NNN'
 
   // print the text alone (sans ''):
   void          method_text(Method* m);
-  void          klass_text(Klass* k);         // klass='name'
-  void          name_text(const Symbol* s);   // name='name'
+  void          klass_text(Klass* k);         // java.lang.String
+  void          symbol_text(const Symbol* s);   // (utf8 bytes)...
+  void          loader_text(oop cl);
   void          object_text(Metadata* x);
   void          object_text(Handle x);
+  void          string_text(oop str);
 
   /*  Example uses:
 
       // Empty element, simple case.
       elem("X Y='Z'");          <X Y='Z'/> \n
@@ -180,8 +203,12 @@
 };
 
 // Standard log file, null if no logging is happening.
 extern xmlStream* xtty;
 
+static inline bool xtty_owns(outputStream* st) {
+  return xtty != nullptr && xtty->owns(st);
+}
+
 // Note:  If ::xtty != nullptr, ::tty == ::xtty->text().
 
 #endif // SHARE_UTILITIES_XMLSTREAM_HPP
diff a/src/hotspot/share/utilities/zipLibrary.cpp b/src/hotspot/share/utilities/zipLibrary.cpp
--- a/src/hotspot/share/utilities/zipLibrary.cpp
+++ b/src/hotspot/share/utilities/zipLibrary.cpp
@@ -34,18 +34,20 @@
  // Entry points in zip.dll for loading zip/jar file entries
 typedef void**(*ZIP_Open_t)(const char* name, char** pmsg);
 typedef void(*ZIP_Close_t)(jzfile* zip);
 typedef jzentry* (*ZIP_FindEntry_t)(jzfile* zip, const char* name, jint* sizeP, jint* nameLen);
 typedef jboolean(*ZIP_ReadEntry_t)(jzfile* zip, jzentry* entry, unsigned char* buf, char* namebuf);
+typedef void(*ZIP_FreeEntry_t)(jzfile *zip, jzentry *entry);
 typedef jint(*ZIP_CRC32_t)(jint crc, const jbyte* buf, jint len);
 typedef const char* (*ZIP_GZip_InitParams_t)(size_t, size_t*, size_t*, int);
 typedef size_t(*ZIP_GZip_Fully_t)(char*, size_t, char*, size_t, char*, size_t, int, char*, char const**);
 
 static ZIP_Open_t ZIP_Open = nullptr;
 static ZIP_Close_t ZIP_Close = nullptr;
 static ZIP_FindEntry_t ZIP_FindEntry = nullptr;
 static ZIP_ReadEntry_t ZIP_ReadEntry = nullptr;
+static ZIP_FreeEntry_t ZIP_FreeEntry = nullptr;
 static ZIP_CRC32_t ZIP_CRC32 = nullptr;
 static ZIP_GZip_InitParams_t ZIP_GZip_InitParams = nullptr;
 static ZIP_GZip_Fully_t ZIP_GZip_Fully = nullptr;
 
 static void* _zip_handle = nullptr;
@@ -74,10 +76,11 @@
   assert(_zip_handle != nullptr, "invariant");
   ZIP_Open = CAST_TO_FN_PTR(ZIP_Open_t, dll_lookup("ZIP_Open", path, vm_exit_on_failure));
   ZIP_Close = CAST_TO_FN_PTR(ZIP_Close_t, dll_lookup("ZIP_Close", path, vm_exit_on_failure));
   ZIP_FindEntry = CAST_TO_FN_PTR(ZIP_FindEntry_t, dll_lookup("ZIP_FindEntry", path, vm_exit_on_failure));
   ZIP_ReadEntry = CAST_TO_FN_PTR(ZIP_ReadEntry_t, dll_lookup("ZIP_ReadEntry", path, vm_exit_on_failure));
+  ZIP_FreeEntry = CAST_TO_FN_PTR(ZIP_FreeEntry_t, dll_lookup("ZIP_FreeEntry", path, vm_exit_on_failure));
   ZIP_CRC32 = CAST_TO_FN_PTR(ZIP_CRC32_t, dll_lookup("ZIP_CRC32", path, vm_exit_on_failure));
   // The following entry points are most likely optional from a zip library implementation perspective.
   // Hence no vm_exit on a resolution failure. Further refactorings should investigate this,
   // and if possible, streamline setting all entry points consistently.
   ZIP_GZip_InitParams = CAST_TO_FN_PTR(ZIP_GZip_InitParams_t, dll_lookup("ZIP_GZip_InitParams", path, false));
@@ -164,10 +167,16 @@
   initialize();
   assert(ZIP_ReadEntry != nullptr, "invariant");
   return ZIP_ReadEntry(zip, entry, buf, namebuf);
 }
 
+void ZipLibrary::free_entry(jzfile* zip, jzentry* entry) {
+  initialize();
+  assert(ZIP_FreeEntry != nullptr, "invariant");
+  ZIP_FreeEntry(zip, entry);
+}
+
 jint ZipLibrary::crc32(jint crc, const jbyte* buf, jint len) {
   initialize();
   assert(ZIP_CRC32 != nullptr, "invariant");
   return ZIP_CRC32(crc, buf, len);
 }
diff a/src/hotspot/share/utilities/zipLibrary.hpp b/src/hotspot/share/utilities/zipLibrary.hpp
--- a/src/hotspot/share/utilities/zipLibrary.hpp
+++ b/src/hotspot/share/utilities/zipLibrary.hpp
@@ -44,10 +44,11 @@
  public:
   static void** open(const char* name, char** pmsg);
   static void close(jzfile* zip);
   static jzentry* find_entry(jzfile* zip, const char* name, jint* sizeP, jint* nameLen);
   static jboolean read_entry(jzfile* zip, jzentry* entry, unsigned char* buf, char* namebuf);
+  static void free_entry(jzfile* zip, jzentry* entry);
   static jint crc32(jint crc, const jbyte* buf, jint len);
   static const char* init_params(size_t block_size, size_t* needed_out_size, size_t* needed_tmp_size, int level);
   static size_t compress(char* in, size_t in_size, char* out, size_t out_size, char* tmp, size_t tmp_size, int level, char* buf, const char** pmsg);
   static void* handle();
 };
diff a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
--- a/src/java.base/share/classes/java/lang/Class.java
+++ b/src/java.base/share/classes/java/lang/Class.java
@@ -460,10 +460,21 @@
     @CallerSensitiveAdapter
     private static Class<?> forName(String className, Class<?> caller)
             throws ClassNotFoundException {
         ClassLoader loader = (caller == null) ? ClassLoader.getSystemClassLoader()
                                               : ClassLoader.getClassLoader(caller);
+        if (loader instanceof BuiltinClassLoader bcl) {
+            if (bcl.usePositiveCache) {
+                Class<?> result = bcl.checkPositiveLookupCache(className);
+                if (result != null) {
+                    return result;
+                }
+            }
+            if (bcl.useNegativeCache && bcl.checkNegativeLookupCache(className)) {
+                throw new ClassNotFoundException(className);
+            }
+        }
         return forName0(className, true, loader, caller);
     }
 
     /**
      * Returns the {@code Class} object associated with the class or
@@ -540,10 +551,21 @@
      * @since     1.2
      */
     public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
         throws ClassNotFoundException
     {
+        if (loader instanceof BuiltinClassLoader bcl) {
+            if (bcl.usePositiveCache) {
+                Class<?> result = bcl.checkPositiveLookupCache(name);
+                if (result != null) {
+                    return result;
+                }
+            }
+           if (bcl.useNegativeCache && bcl.checkNegativeLookupCache(name)) {
+                throw new ClassNotFoundException(name);
+            }
+        }
         return forName0(name, initialize, loader, null);
     }
 
     /** Called after security check for system loader access checks have been made. */
     private static native Class<?> forName0(String name, boolean initialize,
@@ -2783,12 +2805,12 @@
         // offset of Class.annotationData instance field
         private static final long annotationDataOffset
                 = unsafe.objectFieldOffset(Class.class, "annotationData");
 
         static <T> boolean casReflectionData(Class<?> clazz,
-                                             SoftReference<ReflectionData<T>> oldData,
-                                             SoftReference<ReflectionData<T>> newData) {
+                                             ReflectionData<T> oldData,
+                                             ReflectionData<T> newData) {
             return unsafe.compareAndSetReference(clazz, reflectionDataOffset, oldData, newData);
         }
 
         static boolean casAnnotationType(Class<?> clazz,
                                          AnnotationType oldType,
@@ -2832,45 +2854,41 @@
         ReflectionData(int redefinedCount) {
             this.redefinedCount = redefinedCount;
         }
     }
 
-    private transient volatile SoftReference<ReflectionData<T>> reflectionData;
+    private transient volatile ReflectionData<T> reflectionData;
 
     // Incremented by the VM on each call to JVM TI RedefineClasses()
     // that redefines this class or a superclass.
     private transient volatile int classRedefinedCount;
 
     // Lazily create and cache ReflectionData
     private ReflectionData<T> reflectionData() {
-        SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
+        ReflectionData<T> reflectionData = this.reflectionData;
         int classRedefinedCount = this.classRedefinedCount;
-        ReflectionData<T> rd;
         if (reflectionData != null &&
-            (rd = reflectionData.get()) != null &&
-            rd.redefinedCount == classRedefinedCount) {
-            return rd;
+            reflectionData.redefinedCount == classRedefinedCount) {
+            return reflectionData;
         }
         // else no SoftReference or cleared SoftReference or stale ReflectionData
         // -> create and replace new instance
         return newReflectionData(reflectionData, classRedefinedCount);
     }
 
-    private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
+    private ReflectionData<T> newReflectionData(ReflectionData<T> oldReflectionData,
                                                 int classRedefinedCount) {
         while (true) {
             ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
             // try to CAS it...
-            if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
+            if (Atomic.casReflectionData(this, oldReflectionData, rd)) {
                 return rd;
             }
             // else retry
             oldReflectionData = this.reflectionData;
             classRedefinedCount = this.classRedefinedCount;
-            if (oldReflectionData != null &&
-                (rd = oldReflectionData.get()) != null &&
-                rd.redefinedCount == classRedefinedCount) {
+            if (oldReflectionData != null && oldReflectionData.redefinedCount == classRedefinedCount) {
                 return rd;
             }
         }
     }
 
@@ -4174,6 +4192,55 @@
         Class<?> c = isArray() ? elementType() : this;
         return c.getClassAccessFlagsRaw0();
     }
 
     private native int getClassAccessFlagsRaw0();
+
+    // Support for "OLD" CDS workflow -- {
+    private static final int RD_PUBLIC_METHODS          = (1 <<  0);
+    private static final int RD_PUBLIC_FIELDS           = (1 <<  1);
+    private static final int RD_DECLARED_CTORS          = (1 <<  2);
+    private static final int RD_PUBLIC_CTORS = (1 <<  3);
+    private static final int RD_DECLARED_METHODS        = (1 <<  4);
+    private static final int RD_DECLARED_PUBLIC_METHODS = (1 <<  5);
+    private static final int RD_DECLARED_FIELDS         = (1 <<  6);
+    private static final int RD_DECLARED_PUBLIC_FIELDS  = (1 <<  7);
+    private static final int RD_DECLARED_INTERFACES     = (1 <<  8);
+    private static final int RD_DECLARED_SIMPLE_NAME    = (1 <<  9);
+    private static final int RD_DECLARED_CANONICAL_NAME = (1 << 10);
+    private static final int CLS_NAME = (1 << 10);
+
+
+    private int encodeReflectionData() {
+        int flags = CLS_NAME;
+        if (reflectionData != null) {
+            flags = (reflectionData.publicMethods         != null ? RD_PUBLIC_METHODS          : 0) |
+                    (reflectionData.publicFields          != null ? RD_PUBLIC_FIELDS           : 0) |
+                    (reflectionData.declaredConstructors  != null ? RD_DECLARED_CTORS          : 0) |
+                    (reflectionData.publicConstructors    != null ? RD_PUBLIC_CTORS            : 0) |
+                    (reflectionData.declaredMethods       != null ? RD_DECLARED_METHODS        : 0) |
+                    (reflectionData.declaredPublicMethods != null ? RD_DECLARED_PUBLIC_METHODS : 0) |
+                    (reflectionData.declaredFields        != null ? RD_DECLARED_FIELDS         : 0) |
+                    (reflectionData.declaredPublicFields  != null ? RD_DECLARED_PUBLIC_FIELDS  : 0) |
+                    (reflectionData.interfaces            != null ? RD_DECLARED_INTERFACES     : 0) |
+                    (reflectionData.simpleName            != null ? RD_DECLARED_SIMPLE_NAME    : 0) |
+                    (reflectionData.canonicalName         != null ? RD_DECLARED_CANONICAL_NAME : 0);
+        }
+        return flags;
+    }
+    private void generateReflectionData(int flags) {
+        if ((flags & CLS_NAME                  ) != 0) { getName();                             } // String name
+        if ((flags & RD_PUBLIC_METHODS         ) != 0) { privateGetPublicMethods();             } // Method[] publicMethods;
+        if ((flags & RD_PUBLIC_FIELDS          ) != 0) { privateGetPublicFields();              } // Field[] publicFields;
+        if ((flags & RD_DECLARED_CTORS         ) != 0) { privateGetDeclaredConstructors(false); } // Constructor<T>[] declaredConstructors;
+        if ((flags & RD_PUBLIC_CTORS           ) != 0) { privateGetDeclaredConstructors(true);  } // Constructor<T>[] publicConstructors;
+        if ((flags & RD_DECLARED_METHODS       ) != 0) { privateGetDeclaredMethods(false);      } // Method[] declaredMethods;
+        if ((flags & RD_DECLARED_PUBLIC_METHODS) != 0) { privateGetDeclaredMethods(true);       } // Method[] declaredPublicMethods;
+        if ((flags & RD_DECLARED_FIELDS        ) != 0) { privateGetDeclaredFields(false);       } // Field[] declaredFields;
+        if ((flags & RD_DECLARED_PUBLIC_FIELDS ) != 0) { privateGetDeclaredFields(true);        } // Field[] declaredPublicFields;
+        if ((flags & RD_DECLARED_INTERFACES    ) != 0) { getInterfaces(false);                  } // Class<?>[] interfaces;
+        if ((flags & RD_DECLARED_SIMPLE_NAME   ) != 0) { getSimpleName();                       } // String simpleName;
+        if ((flags & RD_DECLARED_CANONICAL_NAME) != 0) { getCanonicalName();                    } // String canonicalName;
+    }
+
+    // -- }
 }
diff a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java
--- a/src/java.base/share/classes/java/lang/ClassLoader.java
+++ b/src/java.base/share/classes/java/lang/ClassLoader.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2019, Azul Systems, Inc. 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
@@ -56,10 +56,11 @@
 import jdk.internal.loader.BuiltinClassLoader;
 import jdk.internal.loader.ClassLoaders;
 import jdk.internal.loader.NativeLibrary;
 import jdk.internal.loader.NativeLibraries;
 import jdk.internal.perf.PerfCounter;
+import jdk.internal.misc.CDS;
 import jdk.internal.misc.Unsafe;
 import jdk.internal.misc.VM;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.CallerSensitiveAdapter;
 import jdk.internal.reflect.Reflection;
@@ -1838,12 +1839,12 @@
             case 3:
                 String msg = "getSystemClassLoader cannot be called during the system class loader instantiation";
                 throw new IllegalStateException(msg);
             default:
                 // system fully initialized
-                assert VM.isBooted() && scl != null;
-                return scl;
+                assert VM.isBooted() && Holder.scl != null;
+                return Holder.scl;
         }
     }
 
     static ClassLoader getBuiltinPlatformClassLoader() {
         return ClassLoaders.platformClassLoader();
@@ -1864,22 +1865,22 @@
             throw new InternalError("system class loader cannot be set at initLevel " +
                                     VM.initLevel());
         }
 
         // detect recursive initialization
-        if (scl != null) {
+        if (Holder.scl != null) {
             throw new IllegalStateException("recursive invocation");
         }
 
         ClassLoader builtinLoader = getBuiltinAppClassLoader();
         String cn = System.getProperty("java.system.class.loader");
         if (cn != null) {
             try {
                 // custom class loader is only supported to be loaded from unnamed module
                 Constructor<?> ctor = Class.forName(cn, false, builtinLoader)
                                            .getDeclaredConstructor(ClassLoader.class);
-                scl = (ClassLoader) ctor.newInstance(builtinLoader);
+                Holder.scl = (ClassLoader) ctor.newInstance(builtinLoader);
             } catch (Exception e) {
                 Throwable cause = e;
                 if (e instanceof InvocationTargetException) {
                     cause = e.getCause();
                     if (cause instanceof Error) {
@@ -1890,13 +1891,13 @@
                     throw (RuntimeException) cause;
                 }
                 throw new Error(cause.getMessage(), cause);
             }
         } else {
-            scl = builtinLoader;
+            Holder.scl = builtinLoader;
         }
-        return scl;
+        return Holder.scl;
     }
 
     // Returns the class's class loader, or null if none.
     static ClassLoader getClassLoader(Class<?> caller) {
         // This can be null if the VM is requesting it
@@ -1905,13 +1906,17 @@
         }
         // Circumvent security check since this is package-private
         return caller.getClassLoader0();
     }
 
-    // The system class loader
-    // @GuardedBy("ClassLoader.class")
-    private static volatile ClassLoader scl;
+    // Holder has the field(s) that need to be initialized during JVM bootstrap even if
+    // the outer is aot-initialized.
+    private static class Holder {
+        // The system class loader
+        // @GuardedBy("ClassLoader.class")
+        private static volatile ClassLoader scl;
+    }
 
     // -- Package --
 
     /**
      * Define a Package of the given Class object.
@@ -2567,14 +2572,28 @@
      */
     private void resetArchivedStates() {
         if (parallelLockMap != null) {
             parallelLockMap.clear();
         }
-        packages.clear();
-        package2certs.clear();
+
+        if (CDS.isDumpingPackages()) {
+            if (System.getProperty("cds.debug.archived.packages") != null) {
+                for (Map.Entry<String, NamedPackage> entry : packages.entrySet()) {
+                    String key = entry.getKey();
+                    NamedPackage value = entry.getValue();
+                    System.out.println("Archiving " + 
+                                       (value instanceof Package ? "Package" : "NamedPackage") +
+                                       " \"" + key + "\" for " + this);
+                }
+            }
+        } else {
+            packages.clear();
+            package2certs.clear();
+        }
         classes.clear();
         classLoaderValueMap = null;
+        libraries.clear();
     }
 }
 
 /*
  * A utility class that will enumerate over an array of enumerations.
diff a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -1639,10 +1639,22 @@
      */
     public static void exit(int status) {
         Runtime.getRuntime().exit(status);
     }
 
+    /**
+     * Returns whether the AOT system is recording training data.
+     *
+     * @return  whether the AOT system is recording training data.
+     */
+    public static native boolean AOTIsTraining();
+
+    /**
+     * Will stop the recording of AOT training data.
+     */
+    public static native void AOTEndTraining();
+
     /**
      * Runs the garbage collector in the Java Virtual Machine.
      * <p>
      * Calling the {@code gc} method suggests that the Java Virtual Machine
      * expend effort toward recycling unused objects in order to
diff a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2025, 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.  Oracle designates this
@@ -2667,10 +2667,15 @@
         }
         return value;
     }
 
     static {
+        runtimeSetup();
+    }
+
+    // Called from JVM when loading an AOT cache
+    private static void runtimeSetup() {
         /**
          * Setup the shared secret to allow code in other packages access
          * private package methods in java.lang.module.
          */
         jdk.internal.access.SharedSecrets
diff a/src/java.base/share/classes/java/lang/reflect/Proxy.java b/src/java.base/share/classes/java/lang/reflect/Proxy.java
--- a/src/java.base/share/classes/java/lang/reflect/Proxy.java
+++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java
@@ -29,10 +29,11 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.WrongMethodTypeException;
 import java.lang.module.ModuleDescriptor;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -48,10 +49,11 @@
 
 import jdk.internal.access.JavaLangAccess;
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.module.Modules;
 import jdk.internal.misc.VM;
+import jdk.internal.misc.CDS;
 import jdk.internal.loader.ClassLoaderValue;
 import jdk.internal.vm.annotation.Stable;
 import sun.reflect.misc.ReflectUtil;
 
 import static java.lang.invoke.MethodType.methodType;
@@ -399,31 +401,51 @@
                 (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
             );
         }
     }
 
+    /**
+     * Called from VM native code during dump time.
+     */
+    private static void initCacheForCDS(ClassLoader platformLoader, ClassLoader appLoader) {
+        ProxyBuilder.initCacheForCDS(platformLoader, appLoader);
+    }
+
     /**
      * Builder for a proxy class.
      *
      * If the module is not specified in this ProxyBuilder constructor,
      * it will map from the given loader and interfaces to the module
      * in which the proxy class will be defined.
      */
     private static final class ProxyBuilder {
         private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 
+        private static String makeProxyClassNamePrefix() {
+            // Allow unique proxy names to be used across CDS dump time and app run time.
+            if (CDS.isDumpingArchive()) {
+                if (CDS.isUsingArchive()) {
+                    return "$Proxy0100"; // CDS dynamic dump
+                } else {
+                    return "$Proxy0010"; // CDS static dump
+                }
+            } else {
+                return "$Proxy";
+            }
+        }
+
         // prefix for all proxy class names
-        private static final String proxyClassNamePrefix = "$Proxy";
+        private static final String proxyClassNamePrefix = makeProxyClassNamePrefix();
 
         // next number to use for generation of unique proxy class names
         private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
         // a reverse cache of defined proxy classes
         private static final ClassLoaderValue<Boolean> reverseProxyCache =
             new ClassLoaderValue<>();
 
-        private record ProxyClassContext(Module module, String packageName, int accessFlags) {
+        private record ProxyClassContext(Module module, String packageName, int accessFlags, boolean isDynamicModule) {
             private ProxyClassContext {
                 if (module.isNamed()) {
                     if (packageName.isEmpty()) {
                         // Per JLS 7.4.2, unnamed package can only exist in unnamed modules.
                         // This means a package-private superinterface exist in the unnamed
@@ -454,23 +476,37 @@
 
         private static Class<?> defineProxyClass(ProxyClassContext context, List<Class<?>> interfaces) {
             /*
              * Choose a name for the proxy class to generate.
              */
+            String packagePrefix = context.packageName().isEmpty()
+                                    ? proxyClassNamePrefix
+                                    : context.packageName() + "." + proxyClassNamePrefix;
+            ClassLoader loader = context.module().getClassLoader();
+            int accessFlags = context.accessFlags() | Modifier.FINAL;
+
+            if (archivedData != null) {
+                Class<?> pc = archivedData.getArchivedProxyClass(loader, packagePrefix, interfaces);
+                if (pc != null) {
+                    reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
+                    return pc;
+                }
+            }
+
             long num = nextUniqueNumber.getAndIncrement();
-            String proxyName = context.packageName().isEmpty()
-                                    ? proxyClassNamePrefix + num
-                                    : context.packageName() + "." + proxyClassNamePrefix + num;
+            String proxyName = packagePrefix + num;
 
-            ClassLoader loader = context.module().getClassLoader();
             trace(proxyName, context.module(), loader, interfaces);
 
+            if (CDS.isLoggingDynamicProxies() && context.isDynamicModule()) {
+                CDS.logDynamicProxy(loader, proxyName, interfaces.toArray(new Class<?>[0]), accessFlags);
+            }
+
             /*
              * Generate the specified proxy class.
              */
-            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces,
-                                                                      context.accessFlags() | Modifier.FINAL);
+            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
             try {
                 Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                               null, "__dynamic_proxy__");
                 reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                 return pc;
@@ -484,10 +520,27 @@
                  */
                 throw new IllegalArgumentException(e.toString());
             }
         }
 
+        /**
+         * Called from VM native code to define a proxy class to be stored in archivedData.
+         */
+        private static Class<?> defineProxyClassForCDS(ClassLoader loader, String proxyName, Class<?>[] interfaces,
+                                                       int accessFlags) {
+            ArrayList<Class<?>> list = new ArrayList<>();
+            for (Object o : interfaces) {
+                list.add((Class<?>)o);
+            }
+
+            ProxyBuilder builder = new ProxyBuilder(loader, list);
+            Constructor<?> cons = builder.build();
+            Class<?> proxyClass = cons.getDeclaringClass();
+            archivedData.putArchivedProxyClass(loader, proxyName, list, proxyClass);
+            return proxyClass;
+        }
+
         /**
          * Test if given class is a class defined by
          * {@link #defineProxyClass(ProxyClassContext, List)}
          */
         static boolean isProxyClass(Class<?> c) {
@@ -747,11 +800,11 @@
                 // opens the package of the non-public proxy class for java.base to access
                 if (targetModule.isNamed()) {
                     Modules.addOpens(targetModule, targetPackageName, Proxy.class.getModule());
                 }
                 // return the module of the package-private interface
-                return new ProxyClassContext(targetModule, targetPackageName, 0);
+                return new ProxyClassContext(targetModule, targetPackageName, 0, false);
             }
 
             // All proxy interfaces are public.  So maps to a dynamic proxy module
             // and add reads edge and qualified exports, if necessary
             Module targetModule = getDynamicModule(loader);
@@ -764,11 +817,11 @@
                 ensureAccess(targetModule, c);
             }
 
             var pkgName = nonExported ? PROXY_PACKAGE_PREFIX + '.' + targetModule.getName()
                                       : targetModule.getName();
-            return new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC);
+            return new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC, true);
         }
 
         /*
          * Ensure the given module can access the given class.
          */
@@ -821,26 +874,174 @@
          * Each class loader will have one dynamic module.
          */
         private static Module getDynamicModule(ClassLoader loader) {
             return dynProxyModules.computeIfAbsent(loader, (ld, clv) -> {
                 // create a dynamic module and setup module access
-                String mn = "jdk.proxy" + counter.incrementAndGet();
+                int num = counter.incrementAndGet();
+                String mn = "jdk.proxy" + num;
                 String pn = PROXY_PACKAGE_PREFIX + "." + mn;
                 ModuleDescriptor descriptor =
                         ModuleDescriptor.newModule(mn, Set.of(SYNTHETIC))
                                         .packages(Set.of(pn, mn))
                                         .exports(mn)
                                         .build();
                 Module m = Modules.defineModule(ld, descriptor, null);
-                Modules.addReads(m, Proxy.class.getModule());
-                Modules.addExports(m, mn);
-                // java.base to create proxy instance and access its Lookup instance
-                Modules.addOpens(m, pn, Proxy.class.getModule());
-                Modules.addOpens(m, mn, Proxy.class.getModule());
+                openDynamicModule(m);
+
+                if (CDS.isDumpingHeap() && archivedData != null) {
+                    archivedData.recordModule(loader, m, num);
+                }
                 return m;
             });
         }
+
+        private static void openDynamicModule(Module m) {
+            String mn = m.getName();
+            String pn = PROXY_PACKAGE_PREFIX + "." + mn;
+            Modules.addReads(m, Proxy.class.getModule());
+            Modules.addExports(m, mn);
+            // java.base to create proxy instance and access its Lookup instance
+            Modules.addOpens(m, pn, Proxy.class.getModule());
+            Modules.addOpens(m, mn, Proxy.class.getModule());
+        }
+
+        static class ArchivedData {
+            static class InterfacesKey {
+                Class<?>[] intfsArray;
+                InterfacesKey(List<Class<?>> intfs) {
+                    intfsArray = new Class<?>[intfs.size()];
+                    for (int i = 0; i < intfs.size(); i++) {
+                        intfsArray[i] = intfs.get(i);
+                    }
+                }
+                @Override
+                public int hashCode() {
+                    return Arrays.hashCode(intfsArray);
+                }
+                @Override
+                public boolean equals(Object other) {
+                    if (other instanceof InterfacesKey) {
+                        InterfacesKey o = (InterfacesKey)other;
+                        int len = intfsArray.length;
+                        if (len != o.intfsArray.length) {
+                            return false;
+                        }
+                        Class<?>[] oa = o.intfsArray;
+                        for (int i = 0; i < len; i++) {
+                            if (intfsArray[i] != oa[i]) {
+                                return false;
+                            }
+                        }
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+
+            ClassLoader platformLoader;
+            ClassLoader appLoader;
+            HashMap<InterfacesKey,Class<?>> bootCache = new HashMap<>();
+            HashMap<InterfacesKey,Class<?>> platformCache = new HashMap<>();
+            HashMap<InterfacesKey,Class<?>> appCache = new HashMap<>();
+
+            Module bootModule;
+            Module platformModule;
+            Module appModule;
+            int maxNum;
+
+            ArchivedData(ClassLoader plat, ClassLoader app) {
+                platformLoader = plat;
+                appLoader = app;
+            }
+
+            HashMap<InterfacesKey,Class<?>> cacheForLoader(ClassLoader loader) {
+                if (loader == null) {
+                    return bootCache;
+                } else if (loader == platformLoader) {
+                    return platformCache;
+                } else if (loader == appLoader) {
+                    return appCache;
+                } else {
+                    return null;
+                }
+            }
+
+            void recordModule(ClassLoader loader, Module m, int num) {
+                if (loader == null) {
+                    bootModule = m;
+                } else if (loader == platformLoader) {
+                    platformModule = m;
+                } else if (loader == appLoader) {
+                    appModule = m;
+                } else {
+                    throw new UnsupportedOperationException("Class loader " + loader + " is not supported");
+                }
+                if (maxNum < num) {
+                    maxNum = num;
+                }
+            }
+
+            void restore() {
+                // The info for addReads/addExports/addOpens are maintained solely inside the VM.
+                // CDS currently doesn't properly archive such info for the dynamically generated modules,
+                // so we have to recreate them at runtime.
+                //
+                // TODO --  consider improving CDS to archive the above info, so we can avoid calling openDynamicModule()
+                if (bootModule != null) {
+                    Module last = dynProxyModules.putIfAbsent(null, bootModule);
+                    assert last == null;
+                    openDynamicModule(bootModule);
+                }
+                if (platformModule != null) {
+                    Module last = dynProxyModules.putIfAbsent(platformLoader, platformModule);
+                    assert last == null;
+                    openDynamicModule(platformModule);
+                }
+                if (appModule != null) {
+                    Module last = dynProxyModules.putIfAbsent(appLoader, appModule);
+                    assert last == null;
+                    openDynamicModule(appModule);
+                }
+
+                while (maxNum > counter.get()) {
+                    counter.incrementAndGet();
+                }
+            }
+
+
+            Class<?> getArchivedProxyClass(ClassLoader loader, String proxyPrefix, List<Class<?>> interfaces) {
+                HashMap<InterfacesKey,Class<?>> cache = cacheForLoader(loader);
+                if (cache != null && cache.size() > 0) {
+                    InterfacesKey key = new InterfacesKey(interfaces);
+                    return cache.get(key);
+                } else {
+                    return null;
+                }
+            }
+
+            void putArchivedProxyClass(ClassLoader loader, String proxyName, List<Class<?>> interfaces, Class<?> cls) {
+                HashMap<InterfacesKey,Class<?>> cache = cacheForLoader(loader);
+                if (cache != null) {
+                    InterfacesKey key = new InterfacesKey(interfaces);
+                    cache.put(key, cls);
+                }
+            }
+        }
+
+        static ArchivedData archivedData;
+
+        static {
+            CDS.initializeFromArchive(ProxyBuilder.class);
+            if (archivedData != null) {
+                archivedData.restore();
+            }
+        }
+
+        private static void initCacheForCDS(ClassLoader platformLoader, ClassLoader appLoader) {
+            archivedData = new ArchivedData(platformLoader, appLoader);
+        }
     }
 
     /**
      * Returns a proxy instance for the specified interfaces
      * that dispatches method invocations to the specified invocation
diff a/src/java.base/share/classes/java/net/URI.java b/src/java.base/share/classes/java/net/URI.java
--- a/src/java.base/share/classes/java/net/URI.java
+++ b/src/java.base/share/classes/java/net/URI.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, 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.  Oracle designates this
@@ -3699,11 +3699,17 @@
 
             return p;
         }
 
     }
+
     static {
+        runtimeSetup();
+    }
+
+    // Called from JVM when loading an AOT cache
+    private static void runtimeSetup() {
         SharedSecrets.setJavaNetUriAccess(
             new JavaNetUriAccess() {
                 public URI create(String scheme, String path) {
                     return new URI(scheme, path);
                 }
diff a/src/java.base/share/classes/java/security/SecureClassLoader.java b/src/java.base/share/classes/java/security/SecureClassLoader.java
--- a/src/java.base/share/classes/java/security/SecureClassLoader.java
+++ b/src/java.base/share/classes/java/security/SecureClassLoader.java
@@ -29,10 +29,11 @@
 
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
+import jdk.internal.misc.CDS;
 
 /**
  * This class extends {@code ClassLoader} with additional support for defining
  * classes with an associated code source and permissions.
  *
@@ -240,8 +241,17 @@
 
     /**
      * Called by the VM, during -Xshare:dump
      */
     private void resetArchivedStates() {
-        pdcache.clear();
+        if (CDS.isDumpingProtectionDomains()) {
+            if (System.getProperty("cds.debug.archived.protection.domains") != null) {
+                for (Map.Entry<CodeSourceKey, ProtectionDomain> entry : pdcache.entrySet()) {
+                    CodeSourceKey key = entry.getKey();
+                    System.out.println("Archiving ProtectionDomain " + key.cs + " for " + this);
+                }
+            }            
+        } else {
+            pdcache.clear();
+        }
     }
 }
diff a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java
--- a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java
+++ b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, 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.  Oracle designates this
@@ -68,13 +68,17 @@
 
     // ClassLoaderValue map for the boot class loader
     private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP
         = new ConcurrentHashMap<>();
 
-    // native libraries loaded by the boot class loader
-    private static final NativeLibraries NATIVE_LIBS
-        = NativeLibraries.newInstance(null);
+    // Holder has the field(s) that need to be initialized during JVM bootstrap even if
+    // the outer is aot-initialized.
+    private static class Holder {
+        // native libraries loaded by the boot class loader
+        private static final NativeLibraries NATIVE_LIBS
+            = NativeLibraries.newInstance(null);
+    }
 
     /**
      * Returns the unnamed module for the boot loader.
      */
     public static Module getUnnamedModule() {
@@ -97,11 +101,11 @@
 
     /**
      * Returns NativeLibraries for the boot class loader.
      */
     public static NativeLibraries getNativeLibraries() {
-        return NATIVE_LIBS;
+        return Holder.NATIVE_LIBS;
     }
 
     /**
      * Returns {@code true} if there is a class path associated with the
      * BootLoader.
diff a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
--- a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
+++ b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
@@ -44,17 +44,19 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 import java.util.stream.Stream;
 
 import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.CDS;
 import jdk.internal.misc.VM;
 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
 import jdk.internal.module.Resources;
 import jdk.internal.vm.annotation.Stable;
 
@@ -98,10 +100,16 @@
     private final BuiltinClassLoader parent;
 
     // the URL class path, or null if there is no class path
     private @Stable URLClassPath ucp;
 
+    private Set<String> negativeLookupCache;
+    private Map<String, Class<? extends Object>> positiveLookupCache;
+
+    public boolean useNegativeCache = false;
+    public boolean usePositiveCache = false;
+
     /**
      * A module defined/loaded by a built-in class loader.
      *
      * A LoadedModule encapsulates a ModuleReference along with its CodeSource
      * URL to avoid needing to create this URL when defining classes.
@@ -190,10 +198,12 @@
         this.parent = parent;
         this.ucp = ucp;
 
         this.nameToModule = new ConcurrentHashMap<>(32);
         this.moduleToReader = new ConcurrentHashMap<>();
+        this.negativeLookupCache = ConcurrentHashMap.newKeySet();
+        this.positiveLookupCache = new ConcurrentHashMap<>();
     }
 
     /**
      * Appends to the given file path to the class path.
      */
@@ -574,13 +584,19 @@
      */
     @Override
     protected Class<?> loadClass(String cn, boolean resolve)
         throws ClassNotFoundException
     {
+        if (useNegativeCache && checkNegativeLookupCache(cn)) {
+            throw new ClassNotFoundException(cn);
+        }
         Class<?> c = loadClassOrNull(cn, resolve);
-        if (c == null)
+        if (c == null) {
+            addToNegativeLookupCache(cn);
             throw new ClassNotFoundException(cn);
+        }
+
         return c;
     }
 
     /**
      * A variation of {@code loadClass} to load a class with the specified
@@ -983,6 +999,53 @@
         resourceCache = null;
         if (!moduleToReader.isEmpty()) {
             moduleToReader.clear();
         }
     }
+
+    public boolean checkNegativeLookupCache(String className) {
+        return negativeLookupCache.contains(className);
+    }
+
+    public void addToNegativeLookupCache(String className) {
+        negativeLookupCache.add(className);
+    }
+
+    public String negativeLookupCacheContents() {
+        String[] contents = negativeLookupCache.toArray(new String[]{});
+        StringBuilder builder = new StringBuilder();
+        if (contents.length != 0) {
+            for (String name: contents) {
+                if (builder.length() != 0) {
+                    builder.append(" ");
+                }
+                builder.append(name);
+            }
+            return builder.toString();
+        } else {
+            return null;
+        }
+    }
+
+    public void generateNegativeLookupCache(String contents) {
+        String[] tokens = contents.split(" ");
+        if (tokens.length > 0) {
+            for (String token : tokens) {
+                negativeLookupCache.add(token);
+            }
+            useNegativeCache = true;
+        }
+    }
+
+    public Class<?> checkPositiveLookupCache(String className) {
+        return positiveLookupCache.get(className);
+    }
+
+    public void generatePositiveLookupCache(Class<?>[] cls) {
+        if (cls.length > 0) {
+            for (Class<?> c : cls) {
+                positiveLookupCache.put(c.getName(), c);
+            }
+            usePositiveCache = true;
+        }
+    }
 }
diff a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
--- a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
+++ b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025, 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.  Oracle designates this
@@ -153,11 +153,11 @@
             if (cached != null) {
                 return cached;
             }
 
             // cannot be loaded by other class loaders
-            if (loadedLibraryNames.contains(name)) {
+            if (Holder.loadedLibraryNames.contains(name)) {
                 throw new UnsatisfiedLinkError("Native Library " + name +
                         " already loaded in another classloader");
             }
 
             /*
@@ -203,11 +203,11 @@
                 }
             } finally {
                 NativeLibraryContext.pop();
             }
             // register the loaded native library
-            loadedLibraryNames.add(name);
+            Holder.loadedLibraryNames.add(name);
             libraries.put(name, lib);
             return lib;
         } finally {
             releaseNativeLibraryLock(name);
         }
@@ -243,10 +243,15 @@
             lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
         }
         return lib;
     }
 
+    // Called at the end of AOTCache assembly phase.
+    public void clear() {
+        libraries.clear();
+    }
+
     private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
         for (String path : paths) {
             File libfile = new File(path, System.mapLibraryName(name));
             NativeLibrary nl = loadLibrary(fromClass, libfile);
             if (nl != null) {
@@ -373,11 +378,11 @@
         @Override
         public void run() {
             acquireNativeLibraryLock(name);
             try {
                 /* remove the native library name */
-                if (!loadedLibraryNames.remove(name)) {
+                if (!Holder.loadedLibraryNames.remove(name)) {
                     throw new IllegalStateException(name + " has already been unloaded");
                 }
                 NativeLibraryContext.push(UNLOADER);
                 try {
                     unload(name, isBuiltin, handle);
@@ -400,13 +405,17 @@
         // The paths searched for libraries
         static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath());
         static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
     }
 
-    // All native libraries we've loaded.
-    private static final Set<String> loadedLibraryNames =
+    // Holder has the fields that need to be initialized during JVM bootstrap even if
+    // the outer is aot-initialized.
+    static class Holder {
+        // All native libraries we've loaded.
+        private static final Set<String> loadedLibraryNames =
             ConcurrentHashMap.newKeySet();
+    }
 
     // reentrant lock class that allows exact counting (with external synchronization)
     @SuppressWarnings("serial")
     private static final class CountedLock extends ReentrantLock {
 
diff a/src/java.base/share/classes/jdk/internal/misc/CDS.java b/src/java.base/share/classes/jdk/internal/misc/CDS.java
--- a/src/java.base/share/classes/jdk/internal/misc/CDS.java
+++ b/src/java.base/share/classes/jdk/internal/misc/CDS.java
@@ -44,10 +44,14 @@
     // Must be in sync with cdsConfig.hpp
     private static final int IS_DUMPING_ARCHIVE              = 1 << 0;
     private static final int IS_DUMPING_STATIC_ARCHIVE       = 1 << 1;
     private static final int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 2;
     private static final int IS_USING_ARCHIVE                = 1 << 3;
+    private static final int IS_DUMPING_HEAP                 = 1 << 4;
+    private static final int IS_LOGGING_DYNAMIC_PROXIES      = 1 << 5;
+    private static final int IS_DUMPING_PACKAGES             = 1 << 6;
+    private static final int IS_DUMPING_PROTECTION_DOMAINS   = 1 << 7;
     private static final int configStatus = getCDSConfigStatus();
 
     /**
      * Should we log the use of lambda form invokers?
      */
@@ -74,10 +78,26 @@
       */
     public static boolean isDumpingStaticArchive() {
         return (configStatus & IS_DUMPING_STATIC_ARCHIVE) != 0;
     }
 
+    public static boolean isDumpingHeap() {
+        return (configStatus & IS_DUMPING_HEAP) != 0;
+    }
+
+    public static boolean isLoggingDynamicProxies() {
+        return (configStatus & IS_LOGGING_DYNAMIC_PROXIES) != 0;
+    }
+
+    public static boolean isDumpingPackages() {
+        return (configStatus & IS_DUMPING_PACKAGES) != 0;
+    }
+
+    public static boolean isDumpingProtectionDomains() {
+        return (configStatus & IS_DUMPING_PROTECTION_DOMAINS) != 0;
+    }
+
     private static native int getCDSConfigStatus();
     private static native void logLambdaFormInvoker(String line);
 
     /**
      * Initialize archived static fields in the given Class using archived
@@ -119,10 +139,19 @@
         if (isLoggingLambdaFormInvokers()) {
             logLambdaFormInvoker(prefix + " " + cn);
         }
     }
 
+    public static void logDynamicProxy(ClassLoader loader, String proxyName,
+                                       Class<?>[] interfaces, int accessFlags) {
+        Objects.requireNonNull(proxyName);
+        Objects.requireNonNull(interfaces);
+        logDynamicProxy0(loader, proxyName, interfaces, accessFlags);
+    }
+    private static native void logDynamicProxy0(ClassLoader loader, String proxyName,
+                                                Class<?>[] interfaces, int accessFlags);
+
     static final String DIRECT_HOLDER_CLASS_NAME  = "java.lang.invoke.DirectMethodHandle$Holder";
     static final String DELEGATING_HOLDER_CLASS_NAME = "java.lang.invoke.DelegatingMethodHandle$Holder";
     static final String BASIC_FORMS_HOLDER_CLASS_NAME = "java.lang.invoke.LambdaForm$Holder";
     static final String INVOKERS_HOLDER_CLASS_NAME = "java.lang.invoke.Invokers$Holder";
 
diff a/src/java.base/share/native/libjava/CDS.c b/src/java.base/share/native/libjava/CDS.c
--- a/src/java.base/share/native/libjava/CDS.c
+++ b/src/java.base/share/native/libjava/CDS.c
@@ -52,10 +52,16 @@
 JNIEXPORT void JNICALL
 Java_jdk_internal_misc_CDS_logLambdaFormInvoker(JNIEnv *env, jclass jcls, jstring line) {
     JVM_LogLambdaFormInvoker(env, line);
 }
 
+JNIEXPORT void JNICALL
+Java_jdk_internal_misc_CDS_logDynamicProxy0(JNIEnv *env, jclass jcls, jobject loader, jstring proxy_name,
+                                            jobjectArray interfaces, jint accessFlags) {
+    JVM_LogDynamicProxy(env, loader, proxy_name, interfaces, accessFlags);
+}
+
 JNIEXPORT void JNICALL
 Java_jdk_internal_misc_CDS_dumpClassList(JNIEnv *env, jclass jcls, jstring fileName) {
     JVM_DumpClassListToFile(env, fileName);
 }
 
diff a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c
--- a/src/java.base/share/native/libjava/System.c
+++ b/src/java.base/share/native/libjava/System.c
@@ -295,10 +295,23 @@
     for (i = 0; i < n; i++) {
         dst[i] = src[i];
     }
 }
 
+JNIEXPORT jboolean JNICALL
+Java_java_lang_System_AOTIsTraining(JNIEnv *env, jclass ign)
+{
+    jboolean isTraining = JVM_AOTIsTraining(env);
+    return isTraining;
+}
+
+JNIEXPORT void JNICALL
+Java_java_lang_System_AOTEndTraining(JNIEnv *env, jclass ign)
+{
+    JVM_AOTEndTraining(env);
+}
+
 JNIEXPORT jstring JNICALL
 Java_java_lang_System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
 {
     int len;
     int prefix_len = (int) strlen(JNI_LIB_PREFIX);
diff a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c
--- a/src/java.base/share/native/libjli/java.c
+++ b/src/java.base/share/native/libjli/java.c
@@ -1338,10 +1338,14 @@
         * Check for CDS option
         */
         if (JLI_StrCmp(arg, "-Xshare:dump") == 0) {
             dumpSharedSpaces = JNI_TRUE;
         }
+        if (JLI_StrCmp(arg, "-XX:AOTMode=create") == 0) {
+            // Alias for -Xshare:dump
+            dumpSharedSpaces = JNI_TRUE;
+        }
     }
 
     if (*pwhat == NULL && --argc >= 0) {
         *pwhat = *argv++;
     }
diff a/src/java.base/share/native/libzip/zip_util.c b/src/java.base/share/native/libzip/zip_util.c
--- a/src/java.base/share/native/libzip/zip_util.c
+++ b/src/java.base/share/native/libzip/zip_util.c
@@ -1116,11 +1116,11 @@
  * Free the given jzentry.
  * In fact we maintain a one-entry cache of the most recently used
  * jzentry for each zip.  This optimizes a common access pattern.
  */
 
-void
+JNIEXPORT void
 ZIP_FreeEntry(jzfile *jz, jzentry *ze)
 {
     jzentry *last;
     ZIP_Lock(jz);
     last = jz->cache;
diff a/src/java.base/share/native/libzip/zip_util.h b/src/java.base/share/native/libzip/zip_util.h
--- a/src/java.base/share/native/libzip/zip_util.h
+++ b/src/java.base/share/native/libzip/zip_util.h
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2023, 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.  Oracle designates this
@@ -274,11 +274,11 @@
 ZIP_Lock(jzfile *zip);
 void
 ZIP_Unlock(jzfile *zip);
 jint
 ZIP_Read(jzfile *zip, jzentry *entry, jlong pos, void *buf, jint len);
-void
+JNIEXPORT void
 ZIP_FreeEntry(jzfile *zip, jzentry *ze);
 jlong ZIP_GetEntryDataOffset(jzfile *zip, jzentry *entry);
 jzentry * ZIP_GetEntry2(jzfile *zip, char *name, jint ulen, jboolean addSlash);
 
 JNIEXPORT jboolean
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/FileMapInfo.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/FileMapInfo.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/FileMapInfo.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/FileMapInfo.java
@@ -114,21 +114,23 @@
 
     populateMetadataTypeArray(db);
   }
 
   private static void populateMetadataTypeArray(TypeDataBase db) {
-    metadataTypeArray = new Type[9];
-
+    metadataTypeArray = new Type[11];
+    // The order needs to match up with CPP_VTABLE_TYPES_DO in src/hotspot/share/cds/cppVtables.cpp
     metadataTypeArray[0] = db.lookupType("ConstantPool");
     metadataTypeArray[1] = db.lookupType("InstanceKlass");
     metadataTypeArray[2] = db.lookupType("InstanceClassLoaderKlass");
     metadataTypeArray[3] = db.lookupType("InstanceMirrorKlass");
     metadataTypeArray[4] = db.lookupType("InstanceRefKlass");
     metadataTypeArray[5] = db.lookupType("InstanceStackChunkKlass");
     metadataTypeArray[6] = db.lookupType("Method");
-    metadataTypeArray[7] = db.lookupType("ObjArrayKlass");
-    metadataTypeArray[8] = db.lookupType("TypeArrayKlass");
+    metadataTypeArray[9] = db.lookupType("MethodData");
+    metadataTypeArray[8] = db.lookupType("MethodCounters");
+    metadataTypeArray[9] = db.lookupType("ObjArrayKlass");
+    metadataTypeArray[10] = db.lookupType("TypeArrayKlass");
   }
 
   public FileMapHeader getHeader() {
     return headerObj;
   }
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java
@@ -146,10 +146,11 @@
         virtualConstructor = new VirtualConstructor(db);
         // Add mappings for all known thread types
         virtualConstructor.addMapping("JavaThread", JavaThread.class);
         if (!VM.getVM().isCore()) {
             virtualConstructor.addMapping("CompilerThread", CompilerThread.class);
+            virtualConstructor.addMapping("TrainingReplayThread", TrainingReplayThread.class);
         }
         virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class);
         virtualConstructor.addMapping("ServiceThread", ServiceThread.class);
         virtualConstructor.addMapping("MonitorDeflationThread", MonitorDeflationThread.class);
         virtualConstructor.addMapping("NotificationThread", NotificationThread.class);
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/TrainingReplayThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/TrainingReplayThread.java
--- /dev/null
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/TrainingReplayThread.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+package sun.jvm.hotspot.runtime;
+
+import sun.jvm.hotspot.debugger.Address;
+
+public class TrainingReplayThread extends JavaThread {
+    public TrainingReplayThread(Address addr) {
+        super(addr);
+    }
+
+    public boolean isHiddenFromExternalView() { return true; }
+}
diff a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfigAccess.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfigAccess.java
--- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfigAccess.java
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfigAccess.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2023, 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.
@@ -294,10 +294,17 @@
             outCppType[0] = entry.type;
         }
         return type.cast(convertValue(name, type, entry.value, inCppType));
     }
 
+    private boolean typeEquals(String t1, String t2) {
+        if (t1.equals("int64_t") && t2.equals("intx")) {
+            return true;
+        }
+        return t1.equals(t2);
+    }
+
     /**
      * Gets a C++ field.
      *
      * @param name fully qualified name of the field
      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
@@ -313,11 +320,11 @@
             }
             throw missingEntry("field", name, store.vmFields.keySet());
         }
 
         // Make sure the native type is still the type we expect.
-        if (cppType != null && !cppType.equals(entry.type)) {
+        if (cppType != null && !typeEquals(cppType, entry.type)) {
             throw new JVMCIError("expected type " + cppType + " but VM field " + name + " is of type " + entry.type);
         }
         return entry;
     }
 
diff a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -194,5 +194,50 @@
 vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEarlyReturn001.java 7199837 generic-all
 
 vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java 8076494 windows-x64
 
 vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/TestDescription.java 8310144 macosx-aarch64
+
+
+
+
+
+
+
+#############################################################################
+
+# leyden/premain branch test failures
+
+#############################################################################
+
+# The following CDS tests fail due to lambdaform/lambdaproxy handling
+runtime/cds/appcds/dynamicArchive/LambdasInTwoArchives.java 8318142 generic-all
+runtime/cds/appcds/dynamicArchive/TestDynamicDumpAtOom.java 8318142 generic-all
+runtime/cds/appcds/dynamicArchive/TestDynamicRegenerateHolderClasses.java 8318142 generic-all
+runtime/cds/appcds/dynamicArchive/TestLambdaInvokers.java 8318142 generic-all
+runtime/cds/appcds/javaldr/ExceptionDuringDumpAtObjectsInitPhase.java 8318142 generic-all
+
+# Windows path handling
+runtime/cds/appcds/applications/SpringPetClinic.java#dynamic 8338613 windows-all
+runtime/cds/appcds/applications/SpringPetClinic.java#leyden 8338613 windows-all
+runtime/cds/appcds/applications/SpringPetClinic.java#leyden_old 8338613 windows-all
+runtime/cds/appcds/applications/SpringPetClinic.java#static 8338613 windows-all
+
+# Windows failures
+runtime/cds/appcds/leyden/LeydenReflection.java 8338604 windows-all
+runtime/cds/appcds/leyden/LeydenReflectionOldWF.java 8338604 windows-all
+
+# Misc failures -- need to investigate
+runtime/logging/ItablesTest.java 8318142 generic-all
+runtime/logging/VerificationTest.java 8318142 generic-all
+runtime/logging/VtablesTest.java 8318142 generic-all
+runtime/MemberName/MemberNameLeak.java 8318142 generic-all
+
+# Not analyzed yet -- don't seem to be serious -- exclude for now
+compiler/blackhole/BlackholeExistingIntrinsicWarningTest.java 8318142 generic-all
+compiler/blackhole/BlackholeNonEmptyWarningTest.java 8318142 generic-all
+compiler/blackhole/BlackholeNonStaticWarningTest.java 8318142 generic-all
+compiler/blackhole/BlackholeNonVoidWarningTest.java 8318142 generic-all
+compiler/jvmci/TestEnableJVMCIProduct.java 8318142 generic-all
+gtest/GTestWrapper.java 8318142 generic-all
+runtime/ErrorHandling/TestDwarf.java#dontCheckDecoder 8318142 generic-all
+serviceability/dcmd/compiler/CodelistTest.java 8318142 generic-all
diff a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -437,19 +437,29 @@
  -runtime/cds/appcds/customLoader \
  -runtime/cds/appcds/dynamicArchive \
  -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java \
  -runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java \
  -runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java \
+ -runtime/cds/appcds/indy/IndyAndJavac.java \
+ -runtime/cds/appcds/indy/IndyStringConcat.java \
+ -runtime/cds/appcds/indy/StringConcatStress2.java \
+ -runtime/cds/appcds/indy/StringConcatStress.java \
  -runtime/cds/appcds/javaldr/ArrayTest.java \
  -runtime/cds/appcds/javaldr/ExceptionDuringDumpAtObjectsInitPhase.java \
  -runtime/cds/appcds/javaldr/GCSharedStringsDuringDump.java \
  -runtime/cds/appcds/javaldr/LockDuringDump.java \
  -runtime/cds/appcds/jcmd/JCmdTestStaticDump.java \
  -runtime/cds/appcds/jcmd/JCmdTestDynamicDump.java \
  -runtime/cds/appcds/jcmd/JCmdTestFileSafety.java \
  -runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.java \
  -runtime/cds/appcds/lambdaForm/DefaultClassListLFInvokers.java \
+ -runtime/cds/appcds/leyden/DynamicProxyTest.java \
+ -runtime/cds/appcds/leyden/ExcludedClasses.java \
+ -runtime/cds/appcds/leyden/LeydenAndOldClasses.java \
+ -runtime/cds/appcds/leyden/LeydenGCFlags.java \
+ -runtime/cds/appcds/leyden/LeydenHello.java \
+ -runtime/cds/appcds/leyden/LeydenReflection.java \
  -runtime/cds/appcds/methodHandles \
  -runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java \
  -runtime/cds/appcds/sharedStrings \
  -runtime/cds/appcds/resolvedConstants \
  -runtime/cds/appcds/ArchiveRelocationTest.java \
diff a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2023, 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.
@@ -53,10 +53,13 @@
     public final long handleDeoptStub = getFieldValue("CompilerToVM::Data::SharedRuntime_deopt_blob_unpack", Long.class, "address");
 
     public final int maxOopMapStackOffset = getFieldValue("CompilerToVM::Data::_max_oop_map_stack_offset", Integer.class, "int");
     public final int heapWordSize = getConstant("HeapWordSize", Integer.class);
 
+    // Check field with intx declaration is the same as int64_t.
+    public final int heldMonitorCountOffset = getFieldOffset("JavaThread::_held_monitor_count", Integer.class, "int64_t");
+
     public final boolean ropProtection;
 
     private Boolean initNmethodEntryBarrierConcurrentPatch(Architecture arch) {
         Boolean patchConcurrent = null;
         if (arch instanceof AArch64 && nmethodEntryBarrier != 0) {
diff a/test/hotspot/jtreg/premain/InvokeDynamic.md b/test/hotspot/jtreg/premain/InvokeDynamic.md
--- /dev/null
+++ b/test/hotspot/jtreg/premain/InvokeDynamic.md
@@ -0,0 +1,340 @@
+# Notes on InvokeDynamic and AOT
+
+
+## Overview
+
+This document is current as of the GIT revision date. It describes the behavior of the branch that contains this document.
+
+CDS Indy optimization is enabled by the `-XX:+AOTInvokeDynamicLinking` flag. This flag
+is enabled by default if you specify `-XX:AOTClassLinking`. It affect only the assembly of the
+static CDS archive. I.e., with `-Xshare:dump` or `-XX:CacheDataStore=<file>.aot`
+
+If you suspect there's a bug, you can explicitly disable this optimization
+with `-XX:+UnlockDiagnosticVMOptions -XX:-AOTInvokeDynamicLinking`, and rerun your tests.
+
+
+Notes:
+
+- Indy call sites are pre-resolved only for the static CDS archive. Therefore,
+  if the AOT wants to generate code for pre-resolved indy call sites in a class X,
+  then X must be in the static CDS archive.
+
+- CDS does not pre-resolve all indys. Instead, it only pre-resolve indys that were actually
+  executed when generating the classlist. Therefore, you should make sure that your classlist generation
+  covers all indy call sites that you want to generate good AOT code for.
+
+- Only two BSMs are supported so far. See `ClassPrelinker::should_preresolve_invokedynamic()` in 
+  [classPrelinker.cpp](../../../../src/hotspot/share/cds/classPrelinker.cpp)
+
+    - `StringConcatFactory::makeConcatWithConstants`
+    - `LambdaMetafactory::metafactory`
+
+
+## Simple Example
+
+
+```
+class ConcatA {
+    static {
+        bar("000", "222");
+        foo("000", "222");
+    }
+
+    public static void main(String args[]) throws Exception {
+        foo("000", "222");
+        System.out.println(x);
+        System.out.println(ConcatB.foo("123" , "abc"));
+
+        doit(() -> {
+                System.out.println("Hello Lambda");
+                Thread.dumpStack();
+            });
+    }
+
+    static void doit(Runnable r) {
+        r.run();
+    }
+
+    static String x;
+    static void foo(String a, String b) {
+        x = a + b;
+    }
+    static void bar(String a, String b) {
+        x = a + b;
+    }
+}
+
+class ConcatB {
+  static String foo(String a, String b) {
+    return "a" + b;
+  }
+}
+```
+
+You can compile it using:
+
+```
+(
+cd ~/tmp
+rm -rf tmpclasses
+mkdir -p tmpclasses
+javac -d tmpclasses ConcatA.java && jar cvf ConcatA.jar -C tmpclasses .
+java -cp ConcatA.jar ConcatA
+)
+```
+
+Then, create classlist, dump static archive with `-XX:+AOTClassLinking` and run it. The `-Xshare:dump` step prints some warnings,
+which can be ignored for now.
+
+
+```
+java -Xshare:off -cp ~/tmp/ConcatA.jar -XX:DumpLoadedClassList=concata.lst ConcatA
+java -Xshare:dump -XX:+AOTClassLinking -XX:SharedClassListFile=concata.lst -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa
+java -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa ConcatA
+```
+
+## Verify that the call sites are archived
+
+To verify that the indy call sites are indeed archived, compare the following output:
+
+```
+java -Xlog:methodhandles -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa ConcatA
+vs
+java -Xlog:methodhandles -cp ~/tmp/ConcatA.jar ConcatA
+```
+
+The second one shows a lot more output than the first one.
+
+You can also use `-XX:+TraceBytecodes`. With `-Xshare:dump -XX:+AOTClassLinking`, you can see that the first
+indy for string concat took only a handful of bytecodes before reaching the `StringConcatHelper.simpleConcat()`
+method. The BSM is completely skipped.
+
+
+```
+[3289356] static void ConcatA.bar(jobject, jobject)
+[3289356]   192633     0  nofast_aload_0
+[3289356]   192634     1  aload_1
+[3289356]   192635     2  invokedynamic bsm=90 54 <makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;>
+
+[3289356] static jobject java.lang.invoke.Invokers$Holder.linkToTargetMethod(jobject, jobject, jobject)
+[3289356]   192636     0  aload_2
+[3289356]   192637     1  checkcast 12 <java/lang/invoke/MethodHandle>
+[3289356]   192638     4  nofast_aload_0
+[3289356]   192639     5  aload_1
+[3289356]   192640     6  invokehandle 45 <java/lang/invoke/MethodHandle.invokeBasic(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;> 
+
+[3289356] static jobject java.lang.invoke.DelegatingMethodHandle$Holder.reinvoke_L(jobject, jobject, jobject)
+[3289356]   192641     0  nofast_aload_0
+[3289356]   192642     1  checkcast 12 <java/lang/invoke/BoundMethodHandle$Species_L>
+[3289356]   192643     4  nofast_getfield 16 <java/lang/invoke/BoundMethodHandle$Species_L.argL0/Ljava/lang/Object;> 
+[3289356]   192644     7  astore_3
+[3289356]   192645     8  aload_3
+[3289356]   192646     9  checkcast 18 <java/lang/invoke/MethodHandle>
+[3289356]   192647    12  aload_1
+[3289356]   192648    13  aload_2
+[3289356]   192649    14  invokehandle 67 <java/lang/invoke/MethodHandle.invokeBasic(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;> 
+
+[3289356] static jobject java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(jobject, jobject, jobject)
+[3289356]   192650     0  nofast_aload_0
+[3289356]   192651     1  invokestatic 16 <java/lang/invoke/DirectMethodHandle.internalMemberName(Ljava/lang/Object;)Ljava/lang/Object;> 
+
+[3289356] static jobject java.lang.invoke.DirectMethodHandle.internalMemberName(jobject)
+[3289356]   192652     0  nofast_aload_0
+[3289356]   192653     1  checkcast 3 <java/lang/invoke/DirectMethodHandle>
+[3289356]   192654     4  nofast_getfield 80 <java/lang/invoke/DirectMethodHandle.member/Ljava/lang/invoke/MemberName;> 
+[3289356]   192655     7  areturn
+
+[3289356] static jobject java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(jobject, jobject, jobject)
+[3289356]   192656     4  astore_3
+[3289356]   192657     5  aload_1
+[3289356]   192658     6  aload_2
+[3289356]   192659     7  aload_3
+[3289356]   192660     8  checkcast 21 <java/lang/invoke/MemberName>
+[3289356]   192661    11  invokestatic 257 <java/lang/invoke/MethodHandle.linkToStatic(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/invoke/MemberName;)Ljava/lang/Object;> 
+
+[3289356] static jobject java.lang.StringConcatHelper.simpleConcat(jobject, jobject)
+```
+
+The same is true for Lambda proxies:
+
+```
+[3289356] static void ConcatA.main(jobject)
+[3289356]   199257    29  invokedynamic bsm=79 42 <run()Ljava/lang/Runnable;>
+
+[3289356] static jobject java.lang.invoke.Invokers$Holder.linkToTargetMethod(jobject)
+[3289356]   199258     0  nofast_aload_0
+[3289356]   199259     1  checkcast 12 <java/lang/invoke/MethodHandle>
+[3289356]   199260     4  invokehandle 56 <java/lang/invoke/MethodHandle.invokeBasic()Ljava/lang/Object;> 
+
+[3289356] static jobject java.lang.invoke.LambdaForm$MH/0x800000003.invoke(jobject)
+[3289356]   199261     0  nofast_aload_0
+[3289356]   199262     1  checkcast 12 <java/lang/invoke/BoundMethodHandle$Species_L>
+[3289356]   199263     4  nofast_getfield 16 <java/lang/invoke/BoundMethodHandle$Species_L.argL0/Ljava/lang/Object;> 
+[3289356]   199264     7  areturn
+
+[3289356] static jobject java.lang.invoke.Invokers$Holder.linkToTargetMethod(jobject)
+[3289356]   199265     7  areturn
+
+[3289356] static void ConcatA.main(jobject)
+[3289356]   199266    34  invokestatic 46 <ConcatA.doit(Ljava/lang/Runnable;)V> 
+
+[3289356] static void ConcatA.doit(jobject)
+[3289356]   199267     0  nofast_aload_0
+[3289356]   199268     1  invokeinterface 50 <java/lang/Runnable.run()V> 
+
+[3289356] virtual void ConcatA$$Lambda/0x800000005.run()
+[3289356]   199269     0  invokestatic 16 <ConcatA.lambda$main$0()V> 
+
+[3289356] static void ConcatA.lambda$main$0()
+[3289356]   199270     0  getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;> 
+[3289356]   199271     3  fast_aldc Hello Lambda
+[3289356]   199272     5  invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V> 
+```
+
+## Inspect the archived call sites
+
+To inspect the archived call sites, the easiest is to call the `findclass` function inside gdb:
+
+
+- `gdb --args java -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa ConcatA`
+- set a breakpoint at `exit_globals`
+- When this breakpoint is hit, do the following. You can see that the appendix objects of the call sites have a "CDS perm index", which can be used by the compiler to inline such objects into AOT code.
+
+
+```
+Thread 2 "java" hit Breakpoint 2, exit_globals () at /jdk3/le1/open/src/hotspot/share/runtime/init.cpp:205
+205	  if (!destructorsCalled) {
+(gdb) call findclass("ConcatA", 0xff)
+
+"Executing findclass"
+flags (bitmask):
+   0x01  - print names of methods
+   0x02  - print bytecodes
+   0x04  - print the address of bytecodes
+   0x08  - print info for invokedynamic
+   0x10  - print info for invokehandle
+
+[  0] 0x0000000800256120 class ConcatA loader data: 0x00007ffff032fe70 for instance a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x00000007ffce0f68}
+0x0000000800256848 method <init> : ()V
+0x0000000800731ae0    0 nofast_aload_0
+0x0000000800731ae1    1 invokespecial 1 <java/lang/Object.<init>()V> 
+0x0000000800731ae4    4 return
+
+0x00000008002568c8 static method <clinit> : ()V
+0x0000000800731b20    0 fast_aldc 000
+0x0000000800731b22    2 fast_aldc 222
+0x0000000800731b24    4 invokestatic 64 <ConcatA.bar(Ljava/lang/String;Ljava/lang/String;)V> 
+0x0000000800731b27    7 fast_aldc 000
+0x0000000800731b29    9 fast_aldc 222
+0x0000000800731b2b   11 invokestatic 11 <ConcatA.foo(Ljava/lang/String;Ljava/lang/String;)V> 
+0x0000000800731b2e   14 return
+
+0x0000000800256948 static method main : ([Ljava/lang/String;)V
+0x0000000800731b70    0 fast_aldc 000
+0x0000000800731b72    2 fast_aldc 222
+0x0000000800731b74    4 invokestatic 11 <ConcatA.foo(Ljava/lang/String;Ljava/lang/String;)V> 
+0x0000000800731b77    7 getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;> 
+0x0000000800731b7a   10 getstatic 23 <ConcatA.x/Ljava/lang/String;> 
+0x0000000800731b7d   13 invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V> 
+0x0000000800731b80   16 getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;> 
+0x0000000800731b83   19 fast_aldc 123
+0x0000000800731b85   21 fast_aldc abc
+0x0000000800731b87   23 invokestatic 37 <ConcatB.foo(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;> 
+0x0000000800731b8a   26 invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V> 
+0x0000000800731b8d   29 invokedynamic bsm=79 42 <run()Ljava/lang/Runnable;>
+  BSM: REF_invokeStatic 80 <java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;> 
+  arguments[3] = {
+     <MethodType> 6 Symbol: '()V' count 65535
+     <MethodHandle of kind 6 index at 88> 88 <ConcatA.lambda$main$0()V> 
+     <MethodType> 6 Symbol: '()V' count 65535
+  }
+  ResolvedIndyEntry: Resolved InvokeDynamic Info:
+ - Method: 0x00000008000f5e40 java.lang.Object java.lang.invoke.Invokers$Holder.linkToTargetMethod(java.lang.Object)
+ - Resolved References Index: 12
+ - CP Index: 42
+ - Num Parameters: 1
+ - Return type: object
+ - Has Appendix: 1
+ - Resolution Failed 0
+ - appendix = 0x00000007ffc93648, CDS perm index = 14038
+0x0000000800731b92   34 invokestatic 46 <ConcatA.doit(Ljava/lang/Runnable;)V> 
+0x0000000800731b95   37 return
+
+0x00000008002569c8 static method doit : (Ljava/lang/Runnable;)V
+0x0000000800731be0    0 nofast_aload_0
+0x0000000800731be1    1 invokeinterface 50 <java/lang/Runnable.run()V> 
+0x0000000800731be6    6 return
+
+0x0000000800256a48 static method foo : (Ljava/lang/String;Ljava/lang/String;)V
+0x0000000800731c28    0 nofast_aload_0
+0x0000000800731c29    1 aload_1
+0x0000000800731c2a    2 invokedynamic bsm=90 54 <makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;>
+  BSM: REF_invokeStatic 91 <java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;> 
+  arguments[1] = {
+     
+  }
+  ResolvedIndyEntry: Resolved InvokeDynamic Info:
+ - Method: 0x00000008000f6540 java.lang.Object java.lang.invoke.Invokers$Holder.linkToTargetMethod(java.lang.Object, java.lang.Object, java.lang.Object)
+ - Resolved References Index: 11
+ - CP Index: 54
+ - Num Parameters: 3
+ - Return type: object
+ - Has Appendix: 1
+ - Resolution Failed 0
+ - appendix = 0x00000007ffc93620, CDS perm index = 14037
+0x0000000800731c2f    7 putstatic 23 <ConcatA.x/Ljava/lang/String;> 
+0x0000000800731c32   10 return
+
+0x0000000800256ac8 static method bar : (Ljava/lang/String;Ljava/lang/String;)V
+0x0000000800731c70    0 nofast_aload_0
+0x0000000800731c71    1 aload_1
+0x0000000800731c72    2 invokedynamic bsm=90 54 <makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;>
+  BSM: REF_invokeStatic 91 <java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;> 
+  arguments[1] = {
+     
+  }
+  ResolvedIndyEntry: Resolved InvokeDynamic Info:
+ - Method: 0x00000008000f6540 java.lang.Object java.lang.invoke.Invokers$Holder.linkToTargetMethod(java.lang.Object, java.lang.Object, java.lang.Object)
+ - Resolved References Index: 10
+ - CP Index: 54
+ - Num Parameters: 3
+ - Return type: object
+ - Has Appendix: 1
+ - Resolution Failed 0
+ - appendix = 0x00000007ffc93508, CDS perm index = 14029
+0x0000000800731c77    7 putstatic 23 <ConcatA.x/Ljava/lang/String;> 
+0x0000000800731c7a   10 return
+
+0x0000000800256b48 static method lambda$main$0 : ()V
+0x0000000800731cb8    0 getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;> 
+0x0000000800731cbb    3 fast_aldc Hello Lambda
+0x0000000800731cbd    5 invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V> 
+0x0000000800731cc0    8 invokestatic 59 <java/lang/Thread.dumpStack()V> 
+0x0000000800731cc3   11 return
+(gdb) 
+```
+
+## Benchmarking with Javac
+
+The benchmark [javac_helloworld](javac_helloworld) measures the total elapsed time of `javac HelloWorld.java` using
+the premain branch vs the JDK mainline. About 180 indy call sites are archived.
+
+As of 2023/08/28, we can see the following improvement. Please see [javac_helloworld/run.sh](javac_helloworld/run.sh)
+for details.
+
+```
+$ bash run.sh $MAINLINE_JAVA $PREMAIN_JAVA
+[...]
+Wall clock time - geomean over 10 runs of 'perf stat -r 16 javac HelloWorld.java'
+Mainline JDK (CDS disabled)     302.86 ms
+Mainline JDK (CDS enabled)      161.34 ms
+Premain Prototype (CDS only)    131.71 ms
+Premain Prototype (CDS + AOT)    92.84 ms
+
+$ grep entries.*archived Javac-static.dump.log
+[0.966s][info ][cds        ] Class  CP entries =  35021, archived =  11400 ( 32.6%)
+[0.966s][info ][cds        ] Field  CP entries =  19668, archived =   5643 ( 28.7%)
+[0.966s][info ][cds        ] Method CP entries =  47213, archived =   3289 (  7.0%)
+[0.966s][info ][cds        ] Indy   CP entries =   1192, archived =    184 ( 15.4%)
+
+```
diff a/test/hotspot/jtreg/premain/Makefile b/test/hotspot/jtreg/premain/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/Makefile
@@ -0,0 +1,25 @@
+# Copyright (c) 2023, 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.
+
+clean:
+	for i in */Makefile; do \
+	    (cd $$(dirname $$i); pwd; make clean); \
+	done
diff a/test/hotspot/jtreg/premain/README.md b/test/hotspot/jtreg/premain/README.md
--- /dev/null
+++ b/test/hotspot/jtreg/premain/README.md
@@ -0,0 +1,48 @@
+# Demos and Documentation for Leyden-premain Protype
+
+
+## Demos
+
+- [simple](simple) script to demonstrate the "5 step" process of training/production run with leyden-premain prototype
+
+- [jmh](jmh) run JMH + specjbb2005-jmh-1.25.jar
+
+- [javac](javac) using javac as a regression test, to compile up to 10000 Java source files
+
+- [javac_helloworld](javac_helloworld) measures the total elapsed time of `javac HelloWorld.java` using
+the premain branch vs the JDK mainline.
+It's a good demonstration of how we can improve start-up time of a complex application.
+
+- [javac_new_workflow](javac_new_workflow) Example of the new "one step training" workflow
+(still under development) where you can generate all the Leyden artifacts with a single
+JVM invocation.
+
+## Benchmarking
+
+We use a small set of benchmarks to demonstrate the performance of the optimizations in the Leyden repo.
+
+| Benchmark  | Source |
+| ------------- | ------------- |
+|[helidon-quickstart-se](helidon-quickstart-se)|https://helidon.io/docs/v4/se/guides/quickstart|
+|[micronaut-first-app](micronaut-first-app)|https://guides.micronaut.io/latest/creating-your-first-micronaut-app-maven-java.html|
+|[quarkus-getting-started](quarkus-getting-started)|https://quarkus.io/guides/getting-started|
+|[spring-boot-getting-started](spring-boot-getting-started)|https://spring.io/guides/gs/spring-boot|
+|[spring-petclinic](spring-petclinic)|https://github.com/spring-projects/spring-petclinic|
+
+See [README.md in the repo root](../../../../README.md) for some sample benchmark results.
+
+## Docs
+
+- [InvokeDynamic.md](InvokeDynamic.md) CDS optimizations for invokedynamic
+
+## Regression Testing
+
+Leyden-specific tests have been added to the following directories in the repo:
+
+- [test/hotspot/jtreg/runtime/cds/appcds/applications](../runtime/cds/appcds/applications)
+- [test/hotspot/jtreg/runtime/cds/appcds/indy](../runtime/cds/appcds/indy)
+- [test/hotspot/jtreg/runtime/cds/appcds/leyden](../runtime/cds/appcds/leyden)
+- [test/hotspot/jtreg/runtime/cds/appcds/preloadedClasses](../runtime/cds/appcds/preloadedClasses)
+
+These test cases can be executed using jtreg. Some of the tests (in the applications directories)
+require binaries to be built separately. Please refer to the script [lib/build-for-jtreg.sh](lib/build-for-jtreg.sh)
diff a/test/hotspot/jtreg/premain/helidon-quickstart-se/.gitignore b/test/hotspot/jtreg/premain/helidon-quickstart-se/.gitignore
--- /dev/null
+++ b/test/hotspot/jtreg/premain/helidon-quickstart-se/.gitignore
@@ -0,0 +1,10 @@
+helidon-quickstart-se
+helidon-quickstart-se-*.zip
+*.cds
+*.cds.code
+*.jsa
+*.log
+*.log.*
+*.classlist
+*.preimage
+tmp.txt
diff a/test/hotspot/jtreg/premain/helidon-quickstart-se/Main.java.diff b/test/hotspot/jtreg/premain/helidon-quickstart-se/Main.java.diff
--- /dev/null
+++ b/test/hotspot/jtreg/premain/helidon-quickstart-se/Main.java.diff
@@ -0,0 +1,49 @@
+*** Main.java.old	2024-04-11 16:17:12.132429143 -0700
+--- Main.java	2024-04-11 16:22:52.143865272 -0700
+***************
+*** 1,6 ****
+--- 1,8 ----
+  
+  package io.helidon.examples.quickstart.se;
+  
++ import java.lang.management.ManagementFactory;
++ import java.lang.management.RuntimeMXBean;
+  
+  import io.helidon.logging.common.LogConfig;
+  import io.helidon.config.Config;
+***************
+*** 29,34 ****
+--- 31,43 ----
+       */
+      public static void main(String[] args) {
+          
++ 	long mainStart = System.currentTimeMillis();
++ 	RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
++ 	// This includes all the time spent inside the JVM before main() is reached
++ 	// (since os::Posix::init is called and initial_time_count is initialized).
++ 	long vmStart = runtimeMXBean.getStartTime();
++ 	long maxBeanOverHead = System.currentTimeMillis() - mainStart;
++ 
+          // load logging configuration
+          LogConfig.configureRuntime();
+  
+***************
+*** 46,51 ****
+--- 55,71 ----
+  
+          System.out.println("WEB server is up! http://localhost:" + server.port() + "/simple-greet");
+  
++ 	long end = System.currentTimeMillis();
++ 	Boolean quit = Boolean.getBoolean("autoQuit");
++ 	if (quit) {
++ 		System.out.println("#### Booted and returned in " + (end - vmStart - maxBeanOverHead) + "ms");
++ 		System.out.println("#### (debug) mainStart = " + mainStart);
++ 		System.out.println("#### (debug) vmStart = " + vmStart);
++ 		System.out.println("#### (debug) before main (mainStart - vmStart) = " + (mainStart - vmStart));
++ 		System.out.println("#### (debug) maxBeanOverHead = " + maxBeanOverHead);
++ 		System.out.println("#### (debug) end = " + end);
++ 		System.exit(0);
++ 	}
+      }
+  
+  
diff a/test/hotspot/jtreg/premain/helidon-quickstart-se/Makefile b/test/hotspot/jtreg/premain/helidon-quickstart-se/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/helidon-quickstart-se/Makefile
@@ -0,0 +1,67 @@
+# Copyright (c) 2024, 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.
+
+#===============================================================================
+# Leyden-premain + Helidon helidon-quickstart-se demo
+# (https://helidon.io/docs/v4/se/guides/quickstart)
+#
+# See ../lib/DemoSupport.gmk for the "make" targets that build the demo, run the
+# demo, and gather benchmarking data.
+#===============================================================================
+
+# The following needs to be defined before including ../lib/DemoSupport.gmk
+DEMO_NAME       = helidon-quickstart-se
+DEMO_SRC        = helidon-quickstart-se/src/main/java/io/helidon/examples/quickstart/se/Main.java
+DEMO_JAR        = helidon-quickstart-se/target/helidon-quickstart-se.jar
+DEMO_CMDLINE    = -DautoQuit=true -Dserver.port=0 -jar ${DEMO_JAR}
+# This is for uploading into Artifactory, usually to a path like
+#    https://xxxx.com/artifactory/yyyy/io/helidon/helidon-quickstart-se/4.0.7/helidon-quickstart-se-4.0.7.zip
+DEMO_ARTIFACT   = helidon-quickstart-se-4.0.7.zip
+
+include ../lib/DemoSupport.gmk
+
+# Create the project using instructions from https://helidon.io/docs/v4/se/guides/quickstart, then
+# patch it to add some start-up timing data.
+${DEMO_SRC}: Main.java.diff
+	JAVA_HOME=${BLDJDK_HOME} mvn -U archetype:generate -DinteractiveMode=false \
+	    -DarchetypeGroupId=io.helidon.archetypes \
+	    -DarchetypeArtifactId=helidon-quickstart-se \
+	    -DarchetypeVersion=4.0.7 \
+	    -DgroupId=io.helidon.examples \
+	    -DartifactId=helidon-quickstart-se \
+	    -Dpackage=io.helidon.examples.quickstart.se
+	patch ${DEMO_SRC} Main.java.diff
+
+
+${DEMO_ARTIFACT}: ${DEMO_JAR}
+	${JAR} cvfM ${DEMO_ARTIFACT} \
+		-C helidon-quickstart-se target/helidon-quickstart-se.jar \
+		-C helidon-quickstart-se target/libs
+
+${DEMO_JAR}: ${DEMO_SRC}
+	cd helidon-quickstart-se && JAVA_HOME=${BLDJDK_HOME} mvn package
+
+clean: clean0
+	rm -rf helidon-quickstart-se ${DEMO_ARTIFACT}
+
+clean0:
+	rm -f *.cds *.cds.code *.classlist *.jsa *~ *.log *.log.* hs_err_*
+
diff a/test/hotspot/jtreg/premain/javac/Makefile b/test/hotspot/jtreg/premain/javac/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac/Makefile
@@ -0,0 +1,3 @@
+clean:
+	rm -f *~ *.jsa* *.log* javac.classlist JavacBench.jar JavacBench.java
+
diff a/test/hotspot/jtreg/premain/javac/javac-test.sh b/test/hotspot/jtreg/premain/javac/javac-test.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac/javac-test.sh
@@ -0,0 +1,476 @@
+# Copyright (c) 2023, 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.
+
+#======================================================================
+# Regression test to run javac with premain prototype, to test the CDS and AOT changes.
+#
+# QUICK START:
+#  bash javac-test.sh -n $JAVA
+#
+#    This shows you all the 5 steps for using the premain prototype:
+#
+#       (STEP 1 of 5) Dump javac.classlist (skipped)
+#       (STEP 2 of 5) Create Static javac-static.jsa (skipped)
+#       (STEP 3 of 4) Run with javac-static.jsa and dump profile in javac-dynamic.jsa (With Training Data Replay) (skipped)
+#       (STEP 4 of 4) Run with javac-dynamic.jsa and generate AOT code (skipped)
+#       (STEP 5 of 5) Final production run: with javac-dynamic.jsa and load AOT code (skipped)
+#
+#  bash javac-test.sh $JAVA
+#    This runs all the above 5 steps
+#======================================================================
+
+# Usage:   bash javac-test.sh $OPTS $JAVA
+#          bash javac-test.sh $OPTS $JAVA $JVMARGS
+#
+# $JAVA must be from inside your JDK build dir. E.g., /your/build/dir/images/jdk/bin/java
+#
+# $OPTS is optional. Examples:
+#
+# - Don't run any tests, just print the command-lines
+#     bash cds-test.sh -n $JAVA
+#
+# - Just run test #4
+#     bash cds-test.sh -r 4 $JAVA
+#
+# - Run test 5, enable the JIT compile log, and loop for 10000 iterations. Also pass -Xmx16g to the JVM.
+#     bash cds-test.sh -r 5 -lcomp -l 10000 $JAVA -Xmx16g
+#
+# See the "while" loop below for all available options
+
+MYDIR=$(cd $(dirname $0); pwd)
+echo script is installed at $MYDIR
+
+LIMIT=
+QUIET=
+LOOPS=100
+EXTRA_ARGS=
+EXIT_WHEN_FAIL=0
+
+while true; do
+    if [[ $1 = "-lcomp" ]]; then
+        # Log compiler
+        EXTRA_ARGS="$EXTRA_ARGS -XX:+PrintCompilation"
+        shift
+    elif [[ $1 = "-lsc" ]]; then
+        # Log shared code
+        EXTRA_ARGS="$EXTRA_ARGS -Xlog:scc -Xlog:scc,nmethod"
+        shift
+    elif [[ $1 = "-r" ]]; then
+        # Limit the run to a single test case
+        LIMIT=$2
+        shift
+        shift
+    elif [[ $1 = "-l" ]]; then
+        # Number of loops for JavacBench
+        LOOPS=$2
+        shift
+        shift
+    elif [[ $1 = "-n" ]]; then
+        # Don't run any test cases, just print their help message and command line
+        LIMIT=0
+        shift
+    elif [[ $1 = "-f" ]]; then
+        # Exit the script with exit code 1 as soon as a test fails
+        EXIT_WHEN_FAIL=1
+        shift
+    elif [[ $1 = "-q" ]]; then
+        # Don't print the help messages and command line of test cases that are skipped
+        QUIET=1
+        shift
+    elif [[ $1 = "-all" ]]; then
+        # Run all test cases (that are not included in the 5 steps)"
+        # If this flag is NOT specified, only the tests with STEP in their description will be executed.
+        DOALL=1
+        shift
+    else
+        break
+    fi
+done
+
+JVM=$1
+
+echo JVM=$JVM
+
+JSA_SUFFIX=-$JVM
+
+# Ioi-specific
+if test "$TESTBED" != ""; then
+    IOI_PREFIX=$(cd $TESTBED/../..; pwd)
+fi
+
+if test "$JVM" = "p"; then
+    # Ioi-specific
+    PRODUCT_BLD=${IOI_PREFIX}
+    IOI_TEST_VM=${IOI_PREFIX}-product/images/jdk
+elif test "$JVM" = "d"; then
+    PRODUCT_BLD=${IOI_PREFIX}
+    IOI_TEST_VM=${IOI_PREFIX}-debug/images/jdk
+elif test "$JVM" = "fd"; then
+    PRODUCT_BLD=${IOI_PREFIX}
+    IOI_TEST_VM=${IOI_PREFIX}-fastdebug/images/jdk
+elif [[ ${JVM}XX =~ /images/jdk/bin/javaXX ]]; then
+    PRODUCT_BLD=$(cd $(dirname $JVM)/../../..; pwd);
+    JSA_SUFFIX=
+else
+    echo "Please specify the location of the JVM (must be in your /build/dir/images/jdk/bin)"
+    exit 1
+fi
+
+JVM_AND_ARGS=$@
+TEST_NUM=0
+
+function test-info () {
+    if test "$DOALL" != "1"; then
+        if [[ "$@" =~ STEP ]] ; then
+            true
+        else
+            return 1
+        fi
+    fi
+
+    TEST_NUM=$(expr $TEST_NUM + 1)
+    declare -gA infos
+    declare -gA cmds
+    cmds[$TEST_NUM]=$CMD
+    infos[$TEST_NUM]=$*
+    local SKIPPED=""
+    if test "$LIMIT" != "" && test "$LIMIT" != "$TEST_NUM"; then
+        SKIPPED=" (skipped)"
+    fi
+    if test "$QUIET" = ""; then
+        echo ======================================================================
+        echo "Test $TEST_NUM $*$SKIPPED"
+        echo $CMD
+    fi
+    if test "$SKIPPED" != ""; then
+        return 1
+    else
+        return 0
+    fi
+}
+
+function record_success () {
+    somepassed=1
+    declare -gA status
+    status[$TEST_NUM]=OK
+    echo "RESULT: $TEST_NUM = OK"
+}
+
+function record_failure () {
+    somefailed=1
+    declare -gA status
+    status[$TEST_NUM]=FAILED
+    echo "RESULT: $TEST_NUM = FAILED"
+    if test "$EXIT_WHEN_FAIL" = "1"; then
+        touch fail-stop.txt
+        exit 1
+    fi
+}
+
+cd $MYDIR
+
+if test ! -f JavacBench.jar; then
+    cat > JavacBench.java <<EOF
+//this file is auto-generated. Do not add to source control and do not edit.
+//import org.openjdk.jmh.annotations.*;
+
+import javax.tools.*;
+import java.io.*;
+import java.net.URI;
+import java.util.*;
+
+//@State(Scope.Benchmark)
+public class JavacBench {
+    static class ClassFile extends SimpleJavaFileObject {
+        private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        protected ClassFile(String name) {
+            super(URI.create("memo:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS);
+        }
+        @Override
+        public ByteArrayOutputStream openOutputStream() { return this.baos; }
+        byte[] toByteArray() { return baos.toByteArray(); }
+    }
+    static class FileManager extends ForwardingJavaFileManager<JavaFileManager> {
+        private Map<String, ClassFile> classesMap = new HashMap<String, ClassFile>();
+        protected FileManager(JavaFileManager fileManager) {
+            super(fileManager);
+        }
+        @Override
+        public ClassFile getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) {
+            ClassFile classFile = new ClassFile(name);
+            classesMap.put(name, classFile);
+            return classFile;
+        }
+        public Map<String, byte[]> getByteCode() {
+            Map<String, byte[]> result = new HashMap<>();
+            for (Map.Entry<String, ClassFile> entry : classesMap.entrySet()) {
+                result.put(entry.getKey(), entry.getValue().toByteArray());
+            }
+            return result;
+        }
+    }
+    static class SourceFile extends SimpleJavaFileObject {
+        private CharSequence sourceCode;
+        public SourceFile(String name, CharSequence sourceCode) {
+            super(URI.create("memo:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
+            this.sourceCode = sourceCode;
+        }
+        @Override
+        public CharSequence getCharContent(boolean ignore) {
+            return this.sourceCode;
+        }
+    }
+
+    public Object compile(int count) {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        DiagnosticCollector<JavaFileObject> ds = new DiagnosticCollector<>();
+        Collection<SourceFile> sourceFiles = sources10k.subList(0, count);
+
+        try (FileManager fileManager = new FileManager(compiler.getStandardFileManager(ds, null, null))) {
+            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, sourceFiles);
+            if (task.call()) {
+                return fileManager.getByteCode();
+            } else {
+                for (Diagnostic<? extends JavaFileObject> d : ds.getDiagnostics()) {
+                  System.out.format("Line: %d, %s in %s", d.getLineNumber(), d.getMessage(null), d.getSource().getName());
+                  }
+                throw new InternalError("compilation failure");
+            }
+        } catch (IOException e) {
+            throw new InternalError(e);
+        }
+    }
+
+    List<SourceFile> sources10k;
+
+    List<SourceFile> generate(int count) {
+        ArrayList<SourceFile> sources = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            sources.add(new SourceFile("HelloWorld" + i,
+                    "public class HelloWorld" + i + " {" +
+                            "    public static void main(String[] args) {" +
+                            "        System.out.println(\"Hellow World!\");" +
+                            "    }" +
+                            "}"));
+        }
+        return sources;
+    }
+    //@Setup
+    public void setup() {
+        sources10k = generate(10_000);
+    }
+
+  public static void main(String args[]) throws Throwable {
+    JavacBench bench = new JavacBench();
+    bench.setup();
+
+    if (args.length >= 0) {
+        int count = Integer.parseInt(args[0]);
+        if (count > 0) {
+            bench.compile(count);
+        }
+    }
+  }
+}
+EOF
+    (set -x
+    rm -rf tmpclasses
+    mkdir -p tmpclasses
+    ${PRODUCT_BLD}/images/jdk/bin/javac -d tmpclasses JavacBench.java || exit 1
+    ${PRODUCT_BLD}/images/jdk/bin/jar cf JavacBench.jar -C tmpclasses . || exit 1
+    rm -rf tmpclasses
+    ) || exit 1
+fi
+
+#----------------------------------------------------------------------
+CMD="$JVM_AND_ARGS \
+    -cp JavacBench.jar -XX:DumpLoadedClassList=javac.classlist \
+    -Xlog:class+load=debug:file=javac.class+load.log \
+    JavacBench 1000"
+test-info "(STEP 1 of 5) Dump javac.classlist" &&
+if $CMD; then
+    ls -l javac.classlist
+    wc -l javac.classlist
+    record_success
+    # remove old log
+    rm -f javac.class+load.log.*
+else
+    record_failure
+fi
+
+JSA=javac-static${JSA_SUFFIX}.jsa
+
+#----------------------------------------------------------------------
+CMD="$JVM_AND_ARGS \
+    -Xshare:dump -XX:SharedArchiveFile=$JSA \
+    -XX:SharedClassListFile=javac.classlist \
+    -XX:+AOTClassLinking \
+    -cp JavacBench.jar \
+    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=javac.staticdump.log::filesize=0"
+test-info "(STEP 2 of 5) Create Static $JSA" &&
+if $CMD; then
+    ls -l $JSA
+    record_success
+    rm -f javac.staticdump.log.*
+else
+    record_failure
+fi
+
+#----------------------------------------------------------------------
+CMD="$JVM_AND_ARGS $EXTRA_ARGS \
+    -XX:SharedArchiveFile=$JSA \
+    -cp JavacBench.jar JavacBench $LOOPS"
+test-info "Run with $JSA" &&
+if $CMD; then
+    record_success
+else
+    record_failure
+fi
+
+#----------------------------------------------------------------------
+CMD="$JVM_AND_ARGS $EXTRA_ARGS \
+    -cp JavacBench.jar JavacBench $LOOPS"
+test-info "Run with default archive" &&
+if $CMD; then
+    record_success
+else
+    record_failure
+fi
+
+#----------------------------------------------------------------------
+CMD="$JVM_AND_ARGS $EXTRA_ARGS -Xshare:off -Xlog:cds=debug \
+    -cp JavacBench.jar JavacBench $LOOPS"
+test-info "Run without CDS" &&
+if $CMD; then
+    record_success
+else
+    record_failure
+fi
+
+TESTS="1 2"
+
+for i in $TESTS; do
+    if test $i = 2; then
+        REPLAY=" (With Training Data Replay)"
+        X1="-XX:+RecordTraining"
+        X2="-XX:+ReplayTraining"
+        STEP3="(STEP 3 of 5) "
+        STEP4="(STEP 4 of 5) "
+        STEP5="(STEP 5 of 5) "
+   fi
+
+    DYNJSA=javac-dynamic1${JSA_SUFFIX}-$i.jsa
+
+    #----------------------------------------------------------------------
+    CMD="$JVM_AND_ARGS $EXTRA_ARGS -Xlog:cds=debug \
+            -cp JavacBench.jar -XX:ArchiveClassesAtExit=$DYNJSA $X1 JavacBench $LOOPS"
+    test-info "Dump CDS dynamic archive$REPLAY" &&
+    if $CMD; then
+            record_success
+        else
+            record_failure
+        fi
+
+
+    #----------------------------------------------------------------------
+    CMD="$JVM_AND_ARGS $EXTRA_ARGS -Xlog:cds \
+        -cp JavacBench.jar -XX:SharedArchiveFile=$DYNJSA $X2 JavacBench $LOOPS"
+    test-info "Use CDS dynamic archive$REPLAY" &&
+    if $CMD; then
+        record_success
+    else
+        record_failure
+    fi
+
+    JSA2=javac-dynamic2${JSA_SUFFIX}-$i.jsa
+    if test "$JSA2" = "javac-dynamic2-2.jsa"; then
+        JSA2=javac-dynamic.jsa
+    fi
+
+    #----------------------------------------------------------------------
+    CMD="$JVM_AND_ARGS $EXTRA_ARGS -Xlog:cds=debug,cds+class=debug:file=javac.dynamicdump.log::filesize=0 \
+        -XX:SharedArchiveFile=$JSA -XX:ArchiveClassesAtExit=$JSA2 $X1 \
+        -cp JavacBench.jar JavacBench $LOOPS"
+    test-info "${STEP3}Run with $JSA and dump profile in $JSA2${REPLAY}" &&
+    if $CMD; then
+        record_success
+    else
+        record_failure
+    fi
+
+    #----------------------------------------------------------------------
+    CMD="$JVM_AND_ARGS $EXTRA_ARGS \
+        -XX:SharedArchiveFile=$JSA2 $X2 \
+        -cp JavacBench.jar JavacBench $LOOPS"
+    test-info "Run with $JSA2 $REPLAY" &&
+    if $CMD; then
+        record_success
+    else
+        record_failure
+    fi
+
+    #----------------------------------------------------------------------
+    CMD="$JVM_AND_ARGS $EXTRA_ARGS -Xlog:scc*=warning:file=javac.scc-store.log::filesize=0 \
+        -XX:SharedArchiveFile=$JSA2 $X2 -XX:+StoreCachedCode -XX:CachedCodeFile=${JSA2}-sc -XX:CachedCodeMaxSize=100M \
+        -cp JavacBench.jar JavacBench $LOOPS"
+    test-info "${STEP4}Run with $JSA2 and generate AOT code" &&
+    if $CMD; then
+        record_success
+    else
+        record_failure
+    fi
+
+    #----------------------------------------------------------------------
+    CMD="$JVM_AND_ARGS $EXTRA_ARGS -Xlog:scc*=warning:file=javac.scc-load.log::filesize=0 \
+        -XX:SharedArchiveFile=$JSA2 $X2 -XX:+LoadCachedCode -XX:CachedCodeFile=${JSA2}-sc \
+        -cp JavacBench.jar JavacBench $LOOPS"
+    test-info "${STEP5}Final production run: with $JSA2 and load AOT code" &&
+    if $CMD; then
+        record_success
+    else
+        record_failure
+    fi
+done
+
+echo ===SUMMARY===
+for (( i=1; i<=$TEST_NUM; i++ )) ; do
+    if test "${status[$i]}" != ""; then
+        echo ......................
+        echo $i ${infos[$i]} = ${status[$i]}
+        echo "    $(echo ${cmds[$i]})"
+    fi
+done
+
+if test "$somefailed" = "1"; then
+    echo ""
+    echo '***********************SOME TESTS FAILED ********************************************'
+    echo ""
+    for (( i=1; i<=$TEST_NUM; i++ )) ; do
+        if test "${status[$i]}" = "FAILED"; then
+            echo $i ${infos[$i]} = ${status[$i]}
+        fi
+    done
+    echo ""
+elif test "$somepassed" = "1"; then
+    echo ""
+    echo '*** all passed *****'
+    echo ""
+fi
diff a/test/hotspot/jtreg/premain/javac_helloworld/HelloWorld.java b/test/hotspot/jtreg/premain/javac_helloworld/HelloWorld.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_helloworld/HelloWorld.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+public class HelloWorld {
+    public static void main(String args[]) {
+        System.out.println("Hello World");
+    }
+}
diff a/test/hotspot/jtreg/premain/javac_helloworld/Makefile b/test/hotspot/jtreg/premain/javac_helloworld/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_helloworld/Makefile
@@ -0,0 +1,4 @@
+clean:
+	rm -rf *.jsa *.jsa-sc *.log* *.classlist *~ Simple.jar tmpclasses HelloWorld.class logs report.csv
+
+
diff a/test/hotspot/jtreg/premain/javac_helloworld/run.sh b/test/hotspot/jtreg/premain/javac_helloworld/run.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_helloworld/run.sh
@@ -0,0 +1,62 @@
+# Copyright (c) 2023, 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.
+
+# This script measure the performance of "javac HelloWorld.java"
+#
+# Specify one or more jvms:
+#
+#     bash run.sh $jvm1
+#     bash run.sh $jvm1 $jvm2 ...
+#
+# It's possible to specify extra parameters for the jvms. E.g.
+#
+#     bash run.sh a/bin/java 'b/bin/java -Xint'  'c/bin/java -Xmixed'
+#
+# See ../lib/bench-lib.sh for sample timing data and more information.
+
+APP=Javac
+CMDLINE="com.sun.tools.javac.Main HelloWorld.java"
+
+source ../lib/bench-lib.sh
+
+# Example of comparing two leyden builds
+#
+# $ bash run.sh /bld/leyden/new/bin/java /bld/leyden/old/bin/java
+# ===report.csv================================================
+# Run,1_xon,1_td,1_aot,2_xon,2_td,2_aot
+# 1,118.68000,95.003000,91.050000,118.69000,99.08000,94.664000
+# 2,117.96000,95.262000,91.223000,120.73000,96.863000,96.67000
+# 3,118.43000,96.11000,90.455000,121.16000,98.138000,94.71000
+# 4,115.742000,98.342000,91.075000,120.956000,99.306000,93.407000
+# 5,118.60000,98.172000,90.635000,121.783000,100.952000,95.33000
+# 6,119.958000,99.836000,90.052000,122.60000,101.492000,94.346000
+# 7,118.391000,99.28000,91.158000,121.96000,101.476000,93.113000
+# 8,121.36000,100.026000,90.705000,124.63000,101.697000,94.93000
+# 9,119.84000,100.23000,91.469000,122.93000,102.776000,96.37000
+# 10,121.15000,101.35000,92.78000,124.60000,103.405000,95.88000
+# ==============================jvm1 /bld/leyden/new/bin/java
+# [1_xon ] Premain Prototype (CDS )                      119.00 ms
+# [1_td  ] Premain Prototype (CDS + Training Data)        98.34 ms
+# [1_aot ] Premain Prototype (CDS + Training Data + AOT)  91.06 ms
+# ==============================jvm2 /bld/leyden/old/bin/java
+# [2_xon ] Premain Prototype (CDS )                      121.99 ms
+# [2_td  ] Premain Prototype (CDS + Training Data)       100.50 ms
+# [2_aot ] Premain Prototype (CDS + Training Data + AOT)  94.94 ms
diff a/test/hotspot/jtreg/premain/javac_new_workflow/HelloWorld.java b/test/hotspot/jtreg/premain/javac_new_workflow/HelloWorld.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_new_workflow/HelloWorld.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+public class HelloWorld {
+    public static void main(String args[]) {
+        System.out.println("Hello World");
+    }
+}
diff a/test/hotspot/jtreg/premain/javac_new_workflow/Makefile b/test/hotspot/jtreg/premain/javac_new_workflow/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_new_workflow/Makefile
@@ -0,0 +1,25 @@
+# Copyright (c) 2023, 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.
+
+clean:
+	rm -f *.classlist *.jsa *.log *.cds *.cds.preimage *.cds.code cmds.txt report.csv *~ HelloWorld.class
+
+
diff a/test/hotspot/jtreg/premain/javac_new_workflow/README.md b/test/hotspot/jtreg/premain/javac_new_workflow/README.md
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_new_workflow/README.md
@@ -0,0 +1,124 @@
+# Proposed Leyden Terminal Stage Workflow
+
+This is a new propsed workflow for the "terminal stage" of the [Leyden
+condenser pipeline](https://openjdk.org/projects/leyden/notes/03-toward-condensers)
+
+- The CDS and AOT caches are automatically generated with a single `java` command.
+
+- The caches are stored in the file specified by the `-XX:CacheDataStore=<app>.cds` option
+    - The implementation is still a work in progress. AOT integration is not done yet.
+    - As an intermediate step, the AOT cache may be stored in a separate file.
+
+- The `-XX:CacheDataStore` option is intended to be a replacement for the existing
+  `-XX:SharedArchiveFile` option.
+
+- We no longer need a separate "training run". Instead, the `-XX:CacheDataStore=<app>.cds`
+  option should be added to the command-line of the production run of your application. For example
+
+    ```
+    java -Xlog:cds -XX:CacheDataStore=javac.cds com.sun.tools.javac.Main ~/tmp/HelloWorld.java
+    ```
+
+- If the specified file doesn't exist, it will be created automatically when the JVM process exits:
+
+    - The loaded classes and their compiler profile are dumped into a temporary file with a `.preimage`
+      prefix. E.g., `javac.cds.preimage`
+    - A JVM subprocess is launched to convert `javac.cds.preimage` to the final CDS image, `javac.cds`
+        - See the end of `MetaspaceShared::preload_and_dump_impl()` in
+          [metaspaceShared.cpp](../../../../../src/hotspot/share/cds/metaspaceShared.cpp)
+
+- In the next run of your application, the `javac.cds` file will be automatically loaded at start-up. Your
+  application will see the benefit of CDS (and soon, AOT).
+
+
+- By default, the following VM options are used when `-XX:CacheDataStore=<app>.cds` is specified. This way, you
+  can automatically use all the Leyden-premain optimizations without specifying any extra flags.
+
+    - `RecordTraining` is set to `true` when the VM is *writing* the `<app>.cds.preimage` file.
+    - `RecordTraining`, `ReplayTraining` and `StoreCachedCode` are set to `true` when the VM is *writing* the final CDS image file.
+    - `ReplayTraining` and `LoadCachedCode` are set to `true` when the VM is *loading* the final CDS image file.
+    - `CachedCodeFile` is set to `<app>.cds.code`.
+
+  However, you can explicitly disable some of these flags for diagnostic purposes. For example, the
+  following command-line will automatically generate `app.cds` and `app.cds.code` on its first run. However, it will
+  only load `app.cds` on subsequent runs, but not `app.cds.code`.
+
+
+    ```
+    java -XX:CacheDataStore=app.cds -XX:-LoadCachedCode -cp app.jar MyApp
+
+    ```
+
+- See [run.sh](run.sh) in this directory for an example of using `-XX:CacheDataStore=<app>.cds`
+
+## Notes
+
+- For applications that do not exit automatically, you may need to hand-craft a training like this, so you
+  app exits voluntarily, to allow the subprocess to be launched to complete the generation of `app.cds`.
+
+    ```
+    rm -f app.cds
+    java -XX:CacheDataStore=app.cds -cp app.jar MyApp -exit-after-start
+    ```
+
+- In the future, we may add a `jcmd` option to connect to a long running JVM and trigger the creation of
+  the CacheDataStore.
+
+- By default, the subprocess is automatically forked at JVM exit. For debugging purpose, you can use the
+  `-XX:+CDSManualFinalImage` option to disable the automatic forking. This allows you to debug the the
+   subprocess more easily.
+    - When `-XX:+CDSManualFinalImage` is specified, the JVM will create only the `<app>.cds.preimage`
+      file at exit. It will then print out a command-line that you can execute manually to create the
+      final `<app>.cds` file.
+
+## AOT Code Generation
+
+AOT support is not fully implemented yet. As of Sep 18, 2023, at the end of `MetaspaceShared::preload_and_dump()`,
+the compiler will be executed to compile a single method, `String::charAt`. The nmethod will be stored inside the
+`CachedCodeFile`.
+
+The intended design is to, at this point, compile all methods that were recorded in the traing data during the
+training run. This is TBD.
+
+## Benchmark
+
+(Sep 11, 2023)
+
+
+- Without `-XX:CacheDataStore`
+
+```
+$ perf stat -r 20 java com.sun.tools.javac.Main HelloWorld.java
+
+ Performance counter stats for 'java com.sun.tools.javac.Main HelloWorld.java' (20 runs):
+
+       643.10 msec task-clock        #   2.374 CPUs utilized    ( +-  0.24% )
+        4,318      context-switches  #   6.800 K/sec            ( +-  1.84% )
+           29      cpu-migrations    #  45.666 /sec             ( +-  5.89% )
+       15,003      page-faults       #  23.625 K/sec            ( +-  0.20% )
+2,936,972,438      cycles            #   4.625 GHz              ( +-  0.24% )
+3,262,915,553      instructions      #   1.12  insn per cycle   ( +-  0.10% )
+  644,286,520      branches          #   1.015 G/sec            ( +-  0.11% )
+   29,099,407      branch-misses     #   4.57% of all branches  ( +-  0.15% )
+
+      0.27091 +- 0.00107 seconds time elapsed  ( +-  0.40% )
+```
+
+- With `-XX:CacheDataStore` (note: AOT is not yet supported)
+
+```
+$ perf stat -r 20 java -XX:+ReplayTraining -XX:CacheDataStore=javac.cds com.sun.tools.javac.Main HelloWorld.java
+
+ Performance counter stats for 'java -XX:+ReplayTraining -XX:CacheDataStore=javac.cds com.sun.tools.javac.Main HelloWorld.java' (20 runs):
+
+       234.72 msec task-clock        #   2.165 CPUs utilized    ( +-  0.29% )
+        1,839      context-switches  #   7.735 K/sec            ( +-  1.22% )
+           14      cpu-migrations    #  58.883 /sec             ( +-  4.13% )
+        9,003      page-faults       #  37.866 K/sec            ( +-  0.22% )
+1,070,819,957      cycles            #   4.504 GHz              ( +-  0.30% )
+1,170,776,369      instructions      #   1.08  insn per cycle   ( +-  0.35% )
+  229,314,097      branches          # 964.471 M/sec            ( +-  0.36% )
+    9,544,981      branch-misses     #   4.09% of all branches  ( +-  0.38% )
+
+     0.108406 +- 0.000844 seconds time elapsed  ( +-  0.78% )
+```
diff a/test/hotspot/jtreg/premain/javac_new_workflow/run.sh b/test/hotspot/jtreg/premain/javac_new_workflow/run.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_new_workflow/run.sh
@@ -0,0 +1,99 @@
+# Copyright (c) 2023, 2024, 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.
+
+# This is a new propsed workflow for the "terminal stage" of the Leyden
+# condenser pipeline (see https://openjdk.org/projects/leyden/notes/03-toward-condensers)
+#
+# The CDS and AOT artifacts are generated in a single step.
+#
+# Please run this script and look at the commands in the generated cmds.txt file.
+
+# Usage:
+#
+# bash run.sh $JAVA
+# bash run.sh $JAVA args ....
+#
+# e.g.
+#
+# bash.sh /jdk3/bld/le4/images/jdk/bin/java
+# bash.sh /jdk3/bld/le4/images/jdk/bin/java -XX:+UnlockDiagnosticVMOptions -XX:-AOTInvokeDynamicLinking
+
+# These options are enabled by default for training run. You can either disable them in the command-line
+# as shown above, or edit the following line
+#
+# The training run uses two JVM processes. We add "pid" to the log to distinguish their output.
+TRAINING_OPTS="${TRAINING_OPTS} -Xlog:scc -Xlog:cds=debug::uptime,tags,pid"
+TRAINING_OPTS="${TRAINING_OPTS} -XX:+AOTClassLinking"
+#TRAINING_OPTS="${TRAINING_OPTS} -XX:+RecordTraining"
+
+# These options are enabled by default for training run. 
+#PRODUCTION_OPTS="${PRODUCTION_OPTS} -XX:+ReplayTraining"
+
+
+launcher=$1
+shift
+
+rm -f cmds.txt
+#========================================
+# One-step training run
+#   Remove javac.cds, so that it will be regenerated
+rm -f javac.cds*
+cmd="$launcher -XX:CacheDataStore=javac.cds $TRAINING_OPTS "$@" com.sun.tools.javac.Main HelloWorld.java"
+
+echo "# commands for training run" >> cmds.txt
+echo "rm -f javac.cds*" >> cmds.txt
+echo $cmd >> cmds.txt
+echo >> cmds.txt
+eval $cmd
+
+#========================================
+# Production run
+#    TODO: add flags for AOT cache
+cmd="$launcher $PRODUCTION_OPTS -Xlog:cds -Xlog:scc -XX:CacheDataStore=javac.cds $@ com.sun.tools.javac.Main HelloWorld.java"
+perfcmd="perf stat -r 20 $(echo $cmd | sed -e 's/ [-]Xlog:[^ ]*//g')"
+echo "Production run: "
+echo "   $cmd"
+echo "   "
+
+rm -f HelloWorld.class
+eval "$cmd"
+
+echo "# command for production run" >> cmds.txt
+echo $cmd >> cmds.txt
+echo >> cmds.txt
+
+echo "# commands for benchmarking" >> cmds.txt
+echo perf stat -r 20 $launcher $@ com.sun.tools.javac.Main HelloWorld.java >> cmds.txt
+echo $perfcmd >> cmds.txt
+echo >> cmds.txt
+
+echo "cat cmds.txt"
+cat cmds.txt
+
+echo ========================================
+echo "Verifying"
+if (set -x; $launcher -Xshare:off -cp . HelloWorld); then
+    echo javac seems to be working correctly with $PRODUCTION_OPTS -XX:CacheDataStore=javac.cds
+else
+    echo "Failed?????????????????"
+    exit 1
+fi
+
diff a/test/hotspot/jtreg/premain/javac_new_workflow/run_bench.sh b/test/hotspot/jtreg/premain/javac_new_workflow/run_bench.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/javac_new_workflow/run_bench.sh
@@ -0,0 +1,62 @@
+# Copyright (c) 2024, 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.
+
+# This script measure the performance of "javac HelloWorld.java"
+#
+# Specify one or more jvms:
+#
+#     bash run.sh $jvm1
+#     bash run.sh $jvm1 $jvm2 ...
+#
+# It's possible to specify extra parameters for the jvms. E.g.
+#
+#     bash run.sh a/bin/java 'b/bin/java -Xint'  'c/bin/java -Xmixed'
+#
+# See ../lib/bench-lib.sh for sample timing data and more information.
+
+APP=Javac
+CMDLINE="com.sun.tools.javac.Main HelloWorld.java"
+
+source ../lib/bench-lib-new-workflow.sh
+
+# Example of comparing two leyden builds
+#
+# $ bash run_bench.sh /bld/leyden/new/bin/java /bld/leyden/old/bin/java
+# ===report.csv================================================
+# Run,1_xoff,1_xon,1_aot,2_xoff,2_xon,2_aot
+# 1,251.04000,144.48000,93.34000,254.87000,150.73000,86.31000
+# 2,241.86000,156.01000,94.01000,262.28000,147.82000,93.03000
+# 3,248.77000,138.17000,84.51000,242.18000,132.86000,87.26000
+# 4,249.83000,162.71000,94.93000,247.70000,152.60000,85.95000
+# 5,249.90000,157.8000,88.98000,239.13000,152.79000,89.16000
+# 6,250.33000,161.91000,84.07000,236.03000,156.12000,85.63000
+# 7,244.62000,122.86000,75.37000,248.32000,142.14000,91.10000
+# 8,255.41000,118.35000,83.61000,233.07000,130.12000,90.43000
+# 9,244.33000,153.18000,81.47000,250.07000,150.15000,91.92000
+# 10,247.32000,129.58000,79.58000,244.58000,154.66000,82.08000
+# ==============================jvm1 /bld/leyden/new/bin/java
+# [1_xoff] Premain JDK (CDS disabled)                   248.31 ms
+# [1_xon ] Premain JDK (CDS enabled)                    143.63 ms
+# [1_aot ] Premain Prototype (CDS + Training Data + AOT)  85.76 ms
+# ==============================jvm2 /bld/leyden/old/bin/java
+# [2_xoff] Premain JDK (CDS disabled)                   245.68 ms
+# [2_xon ] Premain JDK (CDS enabled)                    146.74 ms
+# [2_aot ] Premain Prototype (CDS + Training Data + AOT)  88.23 ms
diff a/test/hotspot/jtreg/premain/jmh/Makefile b/test/hotspot/jtreg/premain/jmh/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/jmh/Makefile
@@ -0,0 +1,3 @@
+clean:
+	rm -rf *.jsa *.jsa-sc *.log* *.classlist *~ xml
+
diff a/test/hotspot/jtreg/premain/jmh/run.sh b/test/hotspot/jtreg/premain/jmh/run.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/jmh/run.sh
@@ -0,0 +1,100 @@
+# Copyright (c) 2023, 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.
+
+# run JMH with premain prototype
+#
+# run all tests
+#      bash run.sh $JAVA
+# run a single test
+#      bash run.sh $JAVA 2
+# clean up
+#      make clean
+
+JAVA=$1; shift 
+JAR=specjbb2005-jmh-1.25.jar
+CMDLINE="-jar $JAR Jbb -t 1 -f 0 -r 1 -i 2 -wi 0"
+APP=specjbb2005-jmh
+
+ONLYTEST=$1
+
+TEST_NUM=0
+
+function do_test () {
+    TEST_NUM=$(expr $TEST_NUM + 1)
+
+    echo ========================================
+    echo Test $TEST_NUM $1
+
+    shift
+    if test "$ONLYTEST" != "" -a "$ONLYTEST" != "$TEST_NUM"; then
+        echo "$@"
+        echo "SKIPPED"
+        return 0
+    else
+        local before_errfiles=$(ls hs_err*  2> /dev/null | sort)
+        echo "$@"
+        if eval "$@"; then
+            true
+        else
+            echo "FAILED; command line was:"
+            echo "$@";
+            exit 1
+        fi
+        local after_errfiles=$(ls hs_err*  2> /dev/null | sort)
+        if test "$before_errfiles" != "$after_errfiles"; then
+            echo "HotSpot crashed (check hs_err errfiles); command line was:"
+            echo "$@"
+            exit 1
+        fi
+        echo "PASSED $TEST_NUM"
+        if test "$ONLYTEST" = "$TEST_NUM"; then
+            echo "Exiting after test $TEST_NUM has passed. Further tests may have been skipped"
+            exit 0
+        fi
+    fi
+}
+
+do_test "(STEP 1 of 5) Dump classlist" \
+        $JAVA -Xshare:off -XX:DumpLoadedClassList=$APP.classlist $CMDLINE
+
+do_test "(STEP 2 of 5) Create Static $APP-static.jsa" \
+    $JAVA -Xshare:dump -XX:SharedArchiveFile=$APP-static.jsa -XX:SharedClassListFile=$APP.classlist -cp $JAR \
+    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=$APP-static.dump.log
+
+if false; then
+    do_test "Run with $APP-static.jsa" \
+            $JAVA -XX:SharedArchiveFile=$APP-static.jsa $CMDLINE
+fi
+
+do_test "(STEP 3 of 5) Run with $APP-static.jsa and dump profile in $APP-dynamic.jsa (With Training Data Replay)" \
+    $JAVA -XX:SharedArchiveFile=$APP-static.jsa -XX:ArchiveClassesAtExit=$APP-dynamic.jsa -XX:+RecordTraining \
+        -Xlog:cds=debug,cds+class=debug:file=$APP-dynamic.dump.log \
+        $CMDLINE
+
+do_test "(STEP 4 of 5) Run with $APP-dynamic.jsa and generate AOT code" \
+    $JAVA -XX:SharedArchiveFile=$APP-dynamic.jsa -XX:+ReplayTraining -XX:+StoreCachedCode \
+        -Xlog:scc*=warning:file=$APP-store-sc.log \
+        -XX:CachedCodeFile=$APP-dynamic.jsa-sc -XX:CachedCodeMaxSize=100M $CMDLINE
+
+do_test "(STEP 5 of 5) Final production run: with $APP-dynamic.jsa and load AOT code" \
+    $JAVA -XX:SharedArchiveFile=$APP-dynamic.jsa -XX:+ReplayTraining -XX:+LoadCachedCode \
+        -Xlog:scc*=warning:file=$APP-load-sc.log \
+        -XX:CachedCodeFile=$APP-dynamic.jsa-sc $CMDLINE
diff a/test/hotspot/jtreg/premain/lib/Bench.gmk b/test/hotspot/jtreg/premain/lib/Bench.gmk
--- /dev/null
+++ b/test/hotspot/jtreg/premain/lib/Bench.gmk
@@ -0,0 +1,87 @@
+# Copyright (c) 2024, 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.
+
+
+# You can choose a different number of loops like this:
+#     make compare_premain_builds LOOPS="0 1 2" PM_OLD=.....
+
+LOOPS = 0 1 2 3 4 5 6 7 8 9 10
+SLEEP = sleep 1;
+
+bench: mainline_vs_premain
+
+mainline_vs_premain: clean0 _mainline_vs_premain
+
+_mainline_vs_premain:
+	rm -f mainline_vs_premain.csv
+	@echo "run,mainline default,mainline custom static CDS,premain custom static CDS only,premain CDS + AOT" | tee -a mainline_vs_premain.csv
+	@first=1; for i in ${LOOPS}; do \
+		rm -f tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= run0.ml 2>&1 | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runs.ml 2>&1 | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runs    2>&1 | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runn    2>&1 | grep Booted >> tmp.txt; \
+		if test "$$first" != "1"; then \
+		    echo $$i$$(cat tmp.txt | sed -e 's/.*in /,/g' -e 's/ms//g') | sed -e 's/ //g' | tee -a mainline_vs_premain.csv; \
+	        fi; \
+		first=0; \
+	done
+	@${MAINLINE_JAVA} ../lib/GithubMDChart.java mainline_vs_premain.md < mainline_vs_premain.csv
+
+# Compare two premain builds running "make run"
+#
+# make PM_OLD=/repos/leyden_old/build/linux-x64/images/jdk \
+#      PM_NEW=/repos/leyden_new/build/linux-x64/images/jdk \
+#      BLDJDK_HOME=/usr/local/jdk21 \
+#      compare_premain_builds
+#
+# Note: you can also use the same build, but use different option to enable/disable certain features. E.g.
+#
+# make PM_OLD=/repo/leyden/build/linux-x64/images/jdk PM_OPTS_NEW=-XX:-ArchivePackages \
+#      PM_NEW=/repo/leyden/build/linux-x64/images/jdk PM_OPTS_NEW=-XX:+ArchivePackages \
+#      BLDJDK_HOME=/my/jdk/21/home
+#      compare_premain_builds
+#
+# Or .. to benchmark a feature that you're developing
+#
+# make PM_OLD=/repos/leyden/build/linux-x64/images/jdk PM_OPTS_OLD="-XX:+UnlockDiagnosticVMOptions -XX:-UseNewCode" \
+#      PM_NEW=/repos/leyden/build/linux-x64/images/jdk PM_OPTS_NEW="-XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode" \
+#      BLDJDK_HOME=/usr/local/jdk21 \
+#      compare_premain_builds
+#
+
+compare_premain_builds: clean0 _compare_premain_builds
+
+_compare_premain_builds:
+	@rm -f compare_premain_builds.csv
+	@echo Old build = ${PM_OLD} with options ${PM_OPTS_OLD}
+	@echo New build = ${PM_NEW} with options ${PM_OPTS_NEW}
+	@echo "Run,Old CDS + AOT,New CDS + AOT" | tee -a compare_premain_builds.csv
+	@first=1; for i in ${LOOPS}; do \
+		rm -f tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_OLD} PM_VER=.old PM_OPTS="${PM_OPTS_OLD}" runn 2>&1 | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_NEW} PM_VER=.new PM_OPTS="${PM_OPTS_NEW}" runn 2>&1 | grep Booted >> tmp.txt; \
+		if test "$$first" != "1"; then \
+		    echo $$i$$(cat tmp.txt | sed -e 's/.*in /,/g' -e 's/ms//g') | sed -e 's/ //g' | tee -a compare_premain_builds.csv; \
+	        fi; \
+		first=0; \
+	done
+	@${MAINLINE_JAVA} ../lib/GithubMDChart.java compare_premain_builds.md < compare_premain_builds.csv
diff a/test/hotspot/jtreg/premain/lib/DemoSupport.gmk b/test/hotspot/jtreg/premain/lib/DemoSupport.gmk
--- /dev/null
+++ b/test/hotspot/jtreg/premain/lib/DemoSupport.gmk
@@ -0,0 +1,179 @@
+# Copyright (c) 2024, 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.
+
+#===============================================================================
+# HOWTO
+#===============================================================================
+
+# Build the app from source code
+#    make app
+#
+# Run with all Leyden optimizations (new workflow)
+#    make run
+#    make runn
+#
+# Run with all CDS static archive only (with Leyden repo)
+#    make runs
+#
+# Run without Leyden optimizations (with Leyden repo)
+#    make run0
+#
+
+#===============================================================================
+# Environment Config
+#===============================================================================
+
+# The best way to set the paths to various JDKs is to use environment variables to
+# set the values for the following variables. E.g.
+#  export BLDJDK_HOME=/home/$USER/jdk21
+#  export MAINLINE_HOME=/home/$USER/repo/jdk/build/linux-x64/images/jdk
+#  export PREMAIN_HOME=/home/$USER/leyden/jdk/build/linux-x64/images/jdk
+#  make run
+
+#  *** NOTE: JDK 21 is needed to build most of the demos
+ifndef BLDJDK_HOME
+BLDJDK_HOME   = /jdk3/official/jdk21
+endif
+
+# Points to your build with https://github.com/openjdk/jdk
+# For comparison purposes, this build should be the latest version of the mainline that
+# has been merged into https://github.com/openjdk/leyden/tree/premain
+ifndef MAINLINE_HOME
+MAINLINE_HOME = /jdk3/bld/rum/images/jdk
+endif
+
+# Points to your build with https://github.com/openjdk/leyden/tree/premain
+ifndef PREMAIN_HOME
+PREMAIN_HOME = /jdk3/bld/le4/images/jdk
+endif
+
+# Override this if your system's MVM version is too old (to point to your own build of mvn, etc)
+ifndef MVN
+MVN = mvn
+endif
+
+#===============================================================================
+# Common Makefile rules for Leyden demos
+#===============================================================================
+
+# Options passed to PREMAIN_JAVA. See compare_premain_builds in ./Bench.gmk
+PM_OPTS =
+
+JAR             = ${BLDJDK_HOME}/bin/jar
+BLDJDK_JAVA     = ${BLDJDK_HOME}/bin/java
+MAINLINE_JAVA   = ${MAINLINE_HOME}/bin/java
+PREMAIN_JAVA    = ${PREMAIN_HOME}/bin/java ${PM_OPTS}
+
+# The following variables should be defined by the main Makefile.
+# See ../helidon-quickstart-se/Makefile for an example.
+
+DEMO_CLASSLIST  = ${DEMO_NAME}$(PM_VER).classlist
+DEMO_JSA        = ${DEMO_NAME}$(PM_VER).jsa
+DEMO_CDS        = ${DEMO_NAME}$(PM_VER).cds
+
+DEMO_ML_CLASSLIST = ${DEMO_NAME}$(PM_VER).ml.classlist
+DEMO_ML_JSA       = ${DEMO_NAME}$(PM_VER).ml.jsa
+
+# Rules for the following targets should be specified in the main Makefile.
+# See ../helidon-quickstart-se/Makefile for an example.
+
+src: ${DEMO_SRC}
+app: ${DEMO_JAR}
+artifact: ${DEMO_ARTIFACT}
+
+#
+# The following rules should be common to all demos
+#
+
+# Run with the build JDK------------------------
+runb: ${DEMO_JAR}
+	${BLDJDK_JAVA} ${DEMO_CMDLINE}
+
+# No optimizations (Leyden repo)----------------
+run0: ${DEMO_JAR}
+	${PREMAIN_JAVA} ${DEMO_CMDLINE}
+
+# Static CDS only (Leyden repo)-----------------
+
+list:  ${DEMO_CLASSLIST}
+jsa:   ${DEMO_JSA}
+
+${DEMO_CLASSLIST}: ${DEMO_JAR}
+	${PREMAIN_JAVA} -Xshare:off -XX:DumpLoadedClassList=${DEMO_CLASSLIST} ${DEMO_CMDLINE}
+
+${DEMO_JSA}: ${DEMO_CLASSLIST}
+	rm -f ${DEMO_JSA}.log
+	${PREMAIN_JAVA} -Xshare:dump -XX:SharedClassListFile=${DEMO_CLASSLIST} -XX:SharedArchiveFile=${DEMO_JSA} \
+	    -XX:+AOTClassLinking \
+	    -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=${DEMO_JSA}.log \
+	    ${DEMO_CMDLINE}
+
+runs:  ${DEMO_JSA}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${DEMO_JSA} ${DEMO_CMDLINE}
+
+# Static CDS only (Leyden repo)-----------------
+
+list.ml:  ${DEMO_ML_CLASSLIST}
+jsa.ml:   ${DEMO_ML_JSA}
+
+${DEMO_ML_CLASSLIST}: ${DEMO_JAR}
+	${MAINLINE_JAVA} -Xshare:off -XX:DumpLoadedClassList=${DEMO_ML_CLASSLIST} ${DEMO_CMDLINE}
+
+${DEMO_ML_JSA}: ${DEMO_ML_CLASSLIST}
+	rm -f ${DEMO_ML_JSA}.log
+	${MAINLINE_JAVA} -Xshare:dump -XX:SharedClassListFile=${DEMO_ML_CLASSLIST} -XX:SharedArchiveFile=${DEMO_ML_JSA} \
+	    -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=${DEMO_ML_JSA}.log \
+	    ${DEMO_CMDLINE}
+
+run0.ml: ${DEMO_JAR}
+	${MAINLINE_JAVA} ${DEMO_CMDLINE}
+
+runs.ml: ${DEMO_ML_JSA}
+	${MAINLINE_JAVA} -XX:SharedArchiveFile=${DEMO_ML_JSA} ${DEMO_CMDLINE}
+
+# Leyden new workflow --------------------------
+
+cds: ${DEMO_CDS}
+
+${DEMO_CDS}: ${DEMO_JAR}
+	rm -f ${DEMO_CDS} ${DEMO_CDS}.log
+	${PREMAIN_JAVA} \
+	    -XX:CacheDataStore=${DEMO_CDS} -XX:CachedCodeMaxSize=512M \
+	    -Xlog:scc,cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=${DEMO_CDS}.log::filesize=0 \
+	     ${DEMO_CMDLINE}
+	ls -l ${DEMO_CDS}
+	ls -l ${DEMO_CDS}.code
+
+run: runn
+
+runn: ${DEMO_CDS}
+	${PREMAIN_JAVA} \
+	    -XX:CacheDataStore=${DEMO_CDS} \
+	    -Xlog:scc*=error \
+	     ${DEMO_CMDLINE}
+
+showenv:
+	@echo "BLDJDK_JAVA   = ${BLDJDK_JAVA}"
+	@echo "MAINLINE_JAVA = ${MAINLINE_JAVA}"
+	@echo "PREMAIN_JAVA  = ${PREMAIN_JAVA}"
+
+
+include ../lib/Bench.gmk
diff a/test/hotspot/jtreg/premain/lib/GithubMDChart.java b/test/hotspot/jtreg/premain/lib/GithubMDChart.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/lib/GithubMDChart.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2024, 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.
+ */
+
+/*
+ Used by DemoSupport.gmk:
+ 
+ - calculate the geomean and stdev of benchmark runs
+ - write markdown snippets for rendering benchmark results as a chart on GitHub .MD files
+
+Example:
+
+helidon-quickstart-se$ tbjava ../lib/GithubMDChart.java < mainline_vs_premain.csv
+run,mainline default,mainline custom static CDS,premain custom static CDS only,premain CDS + AOT
+1,398,244,144,107
+2,387,247,142,108
+3,428,238,143,107
+4,391,252,142,111
+5,417,247,141,107
+6,390,239,139,127
+7,387,247,145,111
+8,387,240,147,110
+9,388,242,147,108
+10,400,242,167,108
+Geomean,397.08,243.76,145.52,110.26
+Stdev,13.55,4.19,7.50,5.73
+
+```mermaid
+---
+config:
+    xyChart:
+        chartOrientation: horizontal
+        height: 300
+---
+xychart-beta
+    x-axis "variant" ["mainline default", "mainline custom static CDS", "premain custom static CDS only", "premain CDS + AOT"]
+    y-axis "Elapsed time (ms, smaller is better)" 0 --> 397
+    bar [397, 244, 146, 110]
+```
+
+*/
+
+import java.io.PrintWriter;
+import java.util.Scanner;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class GithubMDChart {
+    @SuppressWarnings("unchecked")
+    public static void main(String args[]) throws Exception {
+        Scanner input = new Scanner(System.in);
+        String line = input.nextLine();
+        //System.out.println(line);
+        String head[] = line.split(",");
+        Object[] groups = new Object[head.length];
+        String[] geomeans = new String[head.length];
+        for (int i = 0; i < head.length; i++) {
+            groups[i] = new ArrayList<Double>();
+        }
+        while (input.hasNext()) {
+            line = input.nextLine();
+            //System.out.println(line);
+            String parts[] = line.split(",");
+            for (int i = 0; i < head.length; i++) {
+                ArrayList<Double> list =  (ArrayList<Double>)(groups[i]);
+                list.add(Double.valueOf(Double.parseDouble(parts[i])));
+            }
+        }
+
+        for (int i = 0; i < head.length; i++) {
+            ArrayList<Double> list =  (ArrayList<Double>)(groups[i]);
+            if (i == 0) {
+                System.out.print("Geomean");
+            } else {
+                System.out.print(",");
+                geomeans[i] = geomean(list);
+                System.out.print(geomeans[i]);
+            }
+        }
+
+        System.out.println();
+
+        for (int i = 0; i < head.length; i++) {
+            ArrayList<Double> list =  (ArrayList<Double>)(groups[i]);
+            if (i == 0) {
+                System.out.print("Stdev");
+            } else {
+                System.out.print(",");
+                System.out.print(stdev(list));
+            }
+        }
+        System.out.println();
+        System.out.println("Markdown snippets in " + args[0]);
+
+        String[] norm = new String[geomeans.length];
+        for (int i = 1; i < head.length; i++) {
+            double base = Double.parseDouble(geomeans[1]);
+            double me   = Double.parseDouble(geomeans[i]);
+            norm[i] = String.format("%.0f", 1000.0 * me / base);
+        }
+
+        PrintWriter pw = new PrintWriter(args[0]);
+        // Horizontal avoids issues with long names overlapping each other,
+        // and setting a smaller-than-default height makes it easier to overlook and compare.
+        pw.println("""
+        ```mermaid
+        ---
+        config:
+            xyChart:
+                chartOrientation: horizontal
+                height: 300
+        ---
+        xychart-beta
+            x-axis "variant" [$names]
+            y-axis "Elapsed time (ms, smaller is better)" 0 --> $maxtime
+            bar [$geomeans]
+        ```
+        
+        -----------------Normalized---------------------------------------------
+        ```mermaid
+        ---
+        config:
+            xyChart:
+                chartOrientation: horizontal
+                height: 300
+        ---
+        xychart-beta
+            x-axis "variant" [$names]
+            y-axis "Elapsed time (normalized, smaller is better)" 0 --> 1000
+            bar [$norms]
+        ```
+        """
+        .replace("$names", '"' + String.join("\", \"", Arrays.copyOfRange(head, 1, head.length)) + '"')
+        .replace("$maxtime", geomeans[1])
+        .replace("$geomeans", String.join(", ", Arrays.copyOfRange(geomeans, 1, geomeans.length)))
+        .replace("$norms", String.join(", ", Arrays.copyOfRange(norm, 1, norm.length)))
+        );
+        pw.close();
+    }
+
+
+    static String geomean(ArrayList<Double> list) {
+        double log = 0.0d;
+        for (Double d : list) {
+            double v = d.doubleValue();
+            if (v <= 0) {
+                v = 0.000001;
+            }
+            log += Math.log(v);
+        }
+
+	return String.format("%.2f", Math.exp(log / list.size()));
+    }
+
+    static String stdev(ArrayList<Double> list) {
+        double sum = 0.0;
+        for (Double d : list) {
+            sum += d.doubleValue();
+        }
+
+        double length = list.size();
+        double mean = sum / length;
+
+        double stdev = 0.0;
+        for (Double d : list) {
+            stdev += Math.pow(d.doubleValue() - mean, 2);
+        }
+
+	return String.format("%.2f", Math.sqrt(stdev / length));
+    }
+}
diff a/test/hotspot/jtreg/premain/lib/bench-lib-new-workflow.sh b/test/hotspot/jtreg/premain/lib/bench-lib-new-workflow.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/lib/bench-lib-new-workflow.sh
@@ -0,0 +1,322 @@
+# Copyright (c) 2024, 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.
+
+#======================================================================
+# Overview
+#
+# This file provides the framework for running start-up benchmarks that have very
+# short elapsed time.
+#
+# The goal is to be able measure very minor improvements (under 1%) even when there
+# are system noises.
+#
+# - Measure the entire elapsed time with 'perf stat -r 16 bin/java .....'
+# - Interleave the execution of the test JVMs. This way, the effect of system noises 
+#   (CPU overheating, throttling, background tasks) is likely evenly distributed across
+#   the test JVMs.
+# - Save the results in a CSV file so you can load it in a spreadsheet and manually
+#   check for noisy samples.
+#
+# NOTE: you can see the effect of CPU throttling in the sample data below. See the
+# 2_xon column, which goes from 123ms when the CPU is cold to 128ms when the CPU is hot.
+
+#======================================================================
+# Config
+#
+# See ../javac_new_workflow/run_bench.sh for an example of using this file
+#
+# Before sourcing this file
+# The caller script should set up the following variables
+
+# required
+#     CMDLINE
+#     APP
+# optional
+#     HEAP_SIZE
+#     RUNS
+#     REPEAT
+#
+#     LEYDEN_CALLER_OPTS
+#       extra args to be added to Leyden JVM runs
+#       e.g. LEYDEN_CALLER_OPTS='-Xlog:scc=error'
+#     TASKSET
+#       allows pinning of perf runs to specific processors
+#       e.g. setting TASKSET='taskest 0xff0' will execute
+#       benchmark runs as
+#         "taskest 0xff0 perf stat -r $REPEAT $JAVA ... $APP $CMDLINE"
+
+# The number of the outer loop
+if test "$RUNS" = ""; then
+    RUNS=10
+fi
+
+# The number passed to "perf stat -r"
+if test "$REPEAT" = ""; then
+    REPEAT=16
+fi
+
+#======================================================================
+# Example
+#
+# FYI: Results I got on 2024/05/14, AArch64
+# With JDK mainline repo and leyden-premain branch that are pretty up to date.
+#
+# $ cd test/hotspot/jtreg/premain/javac_helloworld
+# $ bash run.sh .../mainline/images/jdk/bin/java .../leyden/images/jdk/bin/java
+# 
+# ===report.csv================================================
+# Run,1_xoff,1_xon,2_xoff,2_xon,2_td,2_aot
+# 1,245.95000,165.35000,253.3000,135.85000,114.08000,80.13000
+# 2,255.98000,172.27000,249.61000,131.03000,111.25000,92.39000
+# 3,244.63000,149.85000,247.29000,138.09000,113.11000,94.04000
+# 4,252.84000,150.57000,247.35000,144.17000,110.42000,93.87000
+# 5,247.72000,156.89000,249.38000,149.35000,111.93000,94.74000
+# 6,259.24000,167.54000,239.00000,147.69000,109.98000,84.89000
+# 7,241.83000,150.16000,248.35000,154.1000,110.58000,91.57000
+# 8,247.79000,143.51000,248.72000,144.88000,108.20000,78.43000
+# 9,254.68000,155.95000,253.50000,134.91000,114.17000,93.27000
+# 10,250.66000,166.34000,260.68000,142.91000,117.24000,87.14000
+# ==============================jvm1 /home/adinn/redhat/openjdk/jdkdev/image-jdk-master/jdk/bin/java
+# [1_xoff] Mainline JDK (CDS disabled)                   250.08 ms
+# [1_xon ] Mainline JDK (CDS enabled)                    157.58 ms
+# ==============================jvm2 /home/adinn/redhat/openjdk/jdkdev/image-premain/jdk/bin/java
+# [2_xoff] Premain JDK (CDS disabled)                    249.66 ms
+# [2_xon ] Premain JDK (CDS enabled)                     142.13 ms
+# [2_td  ] Premain Prototype (CDS + Training Data)        112.07 ms
+# [2_aot ] Premain Prototype (CDS + Training Data + AOT)  88.86 ms
+# 
+#======================================================================
+
+
+# "$@" is the command-line specified by the user. Each element specifies a 
+# JVM (which optionally can contain VM parameters).
+#
+# We automatically detect whether the JVM is from the mainline, or from the leyden
+# repo.
+function main () {
+    do_dump "$@"
+    do_exec "$@"
+    do_summary "$@"
+}
+
+function do_dump () {
+    if test "$NODUMP" != ""; then
+        return
+    fi
+
+    local n=0
+    for i in "$@"; do
+        n=$(expr $n + 1)
+        JAVA="$i"
+        if test "$HEAP_SIZE" != ""; then
+            JAVA="$JAVA $HEAP_SIZE"
+        fi
+        dump_one_jvm $n
+    done
+}
+
+function do_exec () {
+    if test "$NOEXEC" != ""; then
+        return
+    fi
+
+    rm -rf logs
+    mkdir -p logs
+    rm -f report.csv
+
+    REPORT_HEADER='Run'
+
+    local n=0
+    for i in "$@"; do
+        n=$(expr $n + 1)
+        JAVA="$i"
+        if test "$HEAP_SIZE" != ""; then
+            JAVA="$JAVA $HEAP_SIZE"
+        fi
+        local APPID=$APP-jvm$n
+        if test -f $APPID-mainline.classlist; then
+            REPORT_HEADER=${REPORT_HEADER},${n}_xoff
+            REPORT_HEADER=${REPORT_HEADER},${n}_xon
+        else
+            REPORT_HEADER=${REPORT_HEADER},${n}_xoff
+            REPORT_HEADER=${REPORT_HEADER},${n}_xon
+            REPORT_HEADER=${REPORT_HEADER},${n}_td
+            REPORT_HEADER=${REPORT_HEADER},${n}_aot
+        fi
+        #report jvm$n = $JAVA
+    done
+
+    report $REPORT_HEADER
+
+    # The #0 run is to warm up the CPU and is not counted
+    for r in $(seq 0 $RUNS); do
+        if test $r = 0; then
+            echo "RUN $r (warmup - discarded)"
+        else
+            echo "RUN $r of $RUNS"
+        fi
+        RUNLOG=$r
+        local n=0
+        for i in "$@"; do
+            n=$(expr $n + 1)
+            JAVA="$i"
+            if test "$HEAP_SIZE" != ""; then
+                JAVA="$JAVA $HEAP_SIZE"
+            fi
+            echo === jvm$n
+            exec_one_jvm $n $r
+        done
+
+        if test $r -gt 0; then
+            report $RUNLOG
+        else
+            echo $RUNLOG
+        fi
+    done
+}
+
+function dump_one_jvm () {
+    local binjava=$(echo $JAVA | sed -e 's/ .*//g')
+    local APPID=$APP-jvm$1
+
+    if $JAVA --version > /dev/null; then
+        if $JAVA -XX:+PrintFlagsFinal --version | grep CDSManualFinalImage > /dev/null; then
+            local is_premain=1
+            local type_msg="(premain )"
+        else
+            local is_premain=0
+            local type_msg="(mainline)"
+        fi
+    else
+        echo "$JAVA doesn't seem to be a real JVM"
+        exit 1
+    fi
+
+    echo ========================================
+    echo dumping archive "$type_msg" for $JAVA
+    echo ========================================
+
+    if test $is_premain = 0; then
+        echo "(Mainline) Dump classlist"
+        (set -x; $JAVA -Xshare:off -XX:DumpLoadedClassList=$APPID-mainline.classlist $CMDLINE) || exit 1
+
+        echo "(Mainline) Dump CDS archive"
+        rm -f $APPID-mainline-static.dump.log
+        (set -x; $JAVA -Xshare:dump -XX:SharedArchiveFile=$APPID-mainline.jsa -XX:SharedClassListFile=$APPID-mainline.classlist \
+                       -Xlog:cds=debug,cds+class=debug:file=$APPID-mainline-static.dump.log::filesize=0 $CMDLINE) || exit 1
+
+    else
+        echo "(Premain) Dump classlist"
+        (set -x; $JAVA -Xshare:off -XX:DumpLoadedClassList=$APPID-premain.classlist $CMDLINE) || exit 1
+
+        echo "(Premain) Dump CDS archive"
+        rm -f $APPID-premain-static.dump.log
+        (set -x; $JAVA -Xshare:dump -XX:SharedArchiveFile=$APPID-premain.jsa -XX:SharedClassListFile=$APPID-premain.classlist \
+                       -Xlog:cds=debug,cds+class=debug:file=$APPID-premain-static.dump.log::filesize=0 $CMDLINE) || exit 1
+
+
+        LEYDEN_OPTS="$LEYDEN_CALLER_OPTS"
+
+        echo "(Premain) Dump Leyden archive"
+        (set -x; $JAVA -XX:CacheDataStore=$APPID.cds $LEYDEN_OPTS $CMDLINE) || exit 1
+
+    fi
+}
+
+function exec_one_jvm () {
+    local APPID=$APP-jvm$1
+
+    if test -f $APPID-mainline.classlist; then
+        (set -x;
+         $TASKSET perf stat -r $REPEAT $JAVA -Xshare:off $CMDLINE 2> logs/${1}_xoff.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xoff.$2)
+
+        (set -x;
+         $TASKSET perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-mainline.jsa $CMDLINE 2> logs/${1}_xon.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xon.$2)
+    fi
+
+    if test -f $APPID.cds; then
+        LEYDEN_OPTS="$LEYDEN_CALLER_OPTS"
+        (set -x;
+         $TASKSET perf stat -r $REPEAT $JAVA -Xshare:off $CMDLINE 2> logs/${1}_xoff.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xoff.$2)
+
+        (set -x;
+         $TASKSET perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-premain.jsa $CMDLINE 2> logs/${1}_xon.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xon.$2)
+        (set -x;
+         $TASKSET perf stat -r $REPEAT $JAVA -XX:CacheDataStore=$APPID.cds -XX:-LoadCachedCode $LEYDEN_OPTS  $CMDLINE 2> logs/${1}_td.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_td.$2)
+        (set -x;
+         $TASKSET perf stat -r $REPEAT $JAVA -XX:CacheDataStore=$APPID.cds  -XX:+LoadCachedCode $LEYDEN_OPTS  $CMDLINE 2> logs/${1}_aot.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_aot.$2)
+    fi
+}
+
+function get_elapsed () {
+    elapsed=$(bc <<< "scale=3; $(cat $1 | grep elapsed | sed -e 's/[+].*//') * 1000")
+    echo $elapsed >> $1.elapsed
+    echo $elapsed
+}
+
+
+function report () {
+    echo "$@"
+    echo "$@" >> report.csv
+}
+
+function geomean () {
+    printf "%6.2f ms" $(awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++} END{m=tot/c; printf "%.2f\n", E^m}' $*)
+}
+
+
+
+function do_summary () {
+    echo ===report.csv================================================
+    cat report.csv
+
+    mkdir -p logs/warmup
+    mv logs/*.0.elapsed logs/*.0 logs/warmup
+    local n=0
+    for i in "$@"; do
+        n=$(expr $n + 1)
+        local APPID=$APP-jvm$n
+        report ==============================jvm$n $i
+        if test -f $APPID-mainline.classlist; then
+            report "[$(printf %-6s ${n}_xoff)] Mainline JDK (CDS disabled)                   $(geomean logs/${n}_xoff.*.elapsed)"
+            report "[$(printf %-6s ${n}_xon )] Mainline JDK (CDS enabled)                    $(geomean logs/${n}_xon.*.elapsed)"
+        else
+            report "[$(printf %-6s ${n}_xoff)] Premain JDK (CDS disabled)                    $(geomean logs/${n}_xoff.*.elapsed)"
+            report "[$(printf %-6s ${n}_xon )] Premain JDK (CDS enabled)                     $(geomean logs/${n}_xon.*.elapsed)"
+            report "[$(printf %-6s ${n}_td )] Premain Prototype (CDS + Training Data)        $(geomean logs/${n}_td.*.elapsed)"
+            report "[$(printf %-6s ${n}_aot )] Premain Prototype (CDS + Training Data + AOT) $(geomean logs/${n}_aot.*.elapsed)"
+        fi
+    done
+}
+
+main "$@"
diff a/test/hotspot/jtreg/premain/lib/bench-lib.sh b/test/hotspot/jtreg/premain/lib/bench-lib.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/lib/bench-lib.sh
@@ -0,0 +1,316 @@
+# Copyright (c) 2023, 2024, 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.
+
+#======================================================================
+# Overview
+#
+# This file provides the framework for running start-up benchmarks that have very
+# short elapsed time.
+#
+# The goal is to be able measure very minor improvements (under 1%) even when there
+# are system noises.
+#
+# - Measure the entire elapsed time with 'perf stat -r 16 bin/java .....'
+# - Interleave the execution of the test JVMs. This way, the effect of system noises 
+#   (CPU overheating, throttling, background tasks) is likely evenly distributed across
+#   the test JVMs.
+# - Save the results in a CSV file so you can load it in a spreadsheet and manually
+#   check for noisy samples.
+#
+# NOTE: you can see the effect of CPU throttling in the sample data below. See the
+# 2_xon column, which goes from 123ms when the CPU is cold to 128ms when the CPU is hot.
+
+#======================================================================
+# Config
+#
+# See ../javac_helloworld/run.sh for an example of using this file
+#
+# Before sourcing this file
+# The caller script should set up the following variables
+
+# required
+#     CMDLINE
+#     APP
+# optional
+#     HEAP_SIZE
+#     RUNS
+#     REPEAT
+
+# The number of the outer loop
+if test "$RUNS" = ""; then
+    RUNS=10
+fi
+
+# The number passed to "perf stat -r"
+if test "$REPEAT" = ""; then
+    REPEAT=16
+fi
+
+#======================================================================
+# Example
+#
+# FYI: Results I got on 2023/12/21, Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz, 32MB RAM
+# With JDK mainline repo and leyden-premain branch that are pretty up to date.
+#
+# $ cd test/hotspot/jtreg/premain/javac_helloworld
+# $ bash run.sh /bld/mainline/images/jdk/bin/java /bld/leyden/images/jdk/bin/java
+# 
+# ===report.csv================================================
+# Run,1_xoff,1_xon,2_xon,2_td,2_aot
+# 1,313.86000,164.25000,123.031000,96.849000,92.38000
+# 2,324.59000,163.745000,122.550000,97.730000,92.45000
+# 3,313.61000,164.222000,122.91000,100.59000,91.18000
+# 4,316.58000,163.779000,124.673000,101.085000,95.10000
+# 5,313.002000,162.854000,126.240000,102.423000,91.81000
+# 6,314.97000,166.020000,126.148000,101.159000,91.43000
+# 7,313.30000,168.23000,128.19000,101.956000,88.13000
+# 8,314.67000,169.04000,126.168000,102.220000,91.90000
+# 9,313.26000,167.864000,125.856000,102.57000,90.75000
+# 10,318.14000,169.075000,128.055000,103.523000,93.61000
+# ==============================jvm1 /bld/mainline/images/jdk/bin/java
+# [1_xoff] Mainline JDK (CDS disabled)                   315.58 ms
+# [1_xon ] Mainline JDK (CDS enabled)                    165.89 ms
+# ==============================jvm2 /bld/leyden/images/jdk/bin/java
+# [2_xon ] Premain Prototype (CDS )                      125.37 ms
+# [2_td  ] Premain Prototype (CDS + Training Data)       100.99 ms
+# [2_aot ] Premain Prototype (CDS + Training Data + AOT)  91.86 ms
+# 
+#======================================================================
+
+
+# "$@" is the command-line specified by the user. Each element specifies a 
+# JVM (which optionally can contain VM parameters).
+#
+# We automatically detect whether the JVM is from the mainline, or from the leyden
+# repo.
+function main () {
+    do_dump "$@"
+    do_exec "$@"
+    do_summary "$@"
+}
+
+function do_dump () {
+    if test "$NODUMP" != ""; then
+        return
+    fi
+
+    local n=0
+    for i in "$@"; do
+        n=$(expr $n + 1)
+        JAVA="$i"
+        if test "$HEAP_SIZE" != ""; then
+            JAVA="$JAVA $HEAP_SIZE"
+        fi
+        dump_one_jvm $n
+    done
+}
+
+function do_exec () {
+    if test "$NOEXEC" != ""; then
+        return
+    fi
+
+    rm -rf logs
+    mkdir -p logs
+    rm -f report.csv
+
+    REPORT_HEADER='Run'
+
+    local n=0
+    for i in "$@"; do
+        n=$(expr $n + 1)
+        JAVA="$i"
+        if test "$HEAP_SIZE" != ""; then
+            JAVA="$JAVA $HEAP_SIZE"
+        fi
+        local APPID=$APP-jvm$n
+        if test -f $APPID-mainline.classlist; then
+            REPORT_HEADER=${REPORT_HEADER},${n}_xoff
+            REPORT_HEADER=${REPORT_HEADER},${n}_xon
+        else
+            REPORT_HEADER=${REPORT_HEADER},${n}_xon
+            REPORT_HEADER=${REPORT_HEADER},${n}_td
+            REPORT_HEADER=${REPORT_HEADER},${n}_aot
+        fi
+        #report jvm$n = $JAVA
+    done
+
+    report $REPORT_HEADER
+
+    # The #0 run is to warm up the CPU and is not counted
+    for r in $(seq 0 $RUNS); do
+        if test $r = 0; then
+            echo "RUN $r (warmup - discarded)"
+        else
+            echo "RUN $r of $RUNS"
+        fi
+        RUNLOG=$r
+        local n=0
+        for i in "$@"; do
+            n=$(expr $n + 1)
+            JAVA="$i"
+            if test "$HEAP_SIZE" != ""; then
+                JAVA="$JAVA $HEAP_SIZE"
+            fi
+            echo === jvm$n
+            exec_one_jvm $n $r
+        done
+
+        if test $r -gt 0; then
+            report $RUNLOG
+        else
+            echo $RUNLOG
+        fi
+    done
+}
+
+function dump_one_jvm () {
+    local binjava=$(echo $JAVA | sed -e 's/ .*//g')
+    local APPID=$APP-jvm$1
+
+    if $JAVA --version > /dev/null; then
+        if $JAVA -XX:+PrintFlagsFinal --version | grep CDSManualFinalImage > /dev/null; then
+            local is_premain=1
+            local type_msg="(premain 5 step)"
+        else
+            local is_premain=0
+            local type_msg="(mainline      )"
+        fi
+    else
+        echo "$JAVA doesn't seem to be a real JVM"
+        exit 1
+    fi
+
+    echo ========================================
+    echo dumping archive "$type_msg" for $JAVA
+    echo ========================================
+
+    if test $is_premain = 0; then
+        echo "(Mainline) Dump classlist"
+        (set -x; $JAVA -Xshare:off -XX:DumpLoadedClassList=$APPID-mainline.classlist $CMDLINE) || exit 1
+
+        echo "(Mainline) Dump CDS archive"
+        rm -f $APPID-mainline-static.dump.log
+        (set -x; $JAVA -Xshare:dump -XX:SharedArchiveFile=$APPID-mainline.jsa -XX:SharedClassListFile=$APPID-mainline.classlist \
+                       -Xlog:cds=debug,cds+class=debug:file=$APPID-mainline-static.dump.log::filesize=0 $CMDLINE) || exit 1
+
+    else
+        # FIXME -- add support for new workflow
+        LEYDEN_OPTS="-XX:+AOTClassLinking"
+
+        echo "(Premain STEP 1 of 5) Dump classlist"
+        (set -x; $JAVA -Xshare:off -XX:DumpLoadedClassList=$APPID.classlist $CMDLINE) || exit 1
+
+        echo "(Premain STEP 2 of 5) Create Static $APPID-static.jsa"
+        rm -f $APPID-static.dump.log
+        (set -x; $JAVA -Xshare:dump $LEYDEN_OPTS -XX:SharedArchiveFile=$APPID-static.jsa -XX:SharedClassListFile=$APPID.classlist \
+                       -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=$APPID-static.dump.log::filesize=0 $CMDLINE) || exit 1
+
+
+        echo "(Premain STEP 3 of 5) Run with $APPID-static.jsa and dump profile in $APPID-dynamic.jsa (With Training Data Replay)"
+        rm -f $APPID-dynamic.dump.log
+        (set -x; $JAVA -XX:SharedArchiveFile=$APPID-static.jsa -XX:ArchiveClassesAtExit=$APPID-dynamic.jsa -XX:+RecordTraining \
+                       -Xlog:cds=debug,cds+class=debug:file=$APPID-dynamic.dump.log::filesize=0 $CMDLINE) || exit 1
+
+        echo "(Premain  4 of 5) Run with $APPID-dynamic.jsa and generate AOT code"
+        rm -f $APPID-store-sc.log
+        (set -x; $JAVA -XX:SharedArchiveFile=$APPID-dynamic.jsa -XX:+ReplayTraining -XX:+StoreCachedCode \
+                       -Xlog:scc*=warning:file=$APPID-store-sc.log::filesize=0 \
+                       -XX:CachedCodeFile=$APPID-dynamic.jsa-sc -XX:CachedCodeMaxSize=100M $CMDLINE) || exit 1
+
+    fi
+}
+
+function exec_one_jvm () {
+    local APPID=$APP-jvm$1
+
+    if test -f $APPID-mainline.classlist; then
+        (set -x;
+         perf stat -r $REPEAT $JAVA -Xshare:off $CMDLINE 2> logs/${1}_xoff.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xoff.$2)
+
+        (set -x;
+         perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-mainline.jsa $CMDLINE 2> logs/${1}_xon.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xon.$2)
+    fi
+
+    if test -f $APPID.classlist; then
+        (set -x;
+         perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-static.jsa $CMDLINE 2> logs/${1}_xon.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_xon.$2)
+        (set -x;
+         perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-dynamic.jsa -XX:+ReplayTraining \
+              $CMDLINE 2> logs/${1}_td.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_td.$2)
+        (set -x;
+         perf stat -r $REPEAT $JAVA -XX:SharedArchiveFile=$APPID-dynamic.jsa -XX:+ReplayTraining -XX:+LoadCachedCode \
+              -XX:CachedCodeFile=$APPID-dynamic.jsa-sc -Xlog:scc=error \
+              $CMDLINE 2> logs/${1}_aot.$2
+        )
+        RUNLOG=$RUNLOG,$(get_elapsed logs/${1}_aot.$2)
+    fi
+}
+
+function get_elapsed () {
+    elapsed=$(bc <<< "scale=3; $(cat $1 | grep elapsed | sed -e 's/[+].*//') * 1000")
+    echo $elapsed >> $1.elapsed
+    echo $elapsed
+}
+
+
+function report () {
+    echo "$@"
+    echo "$@" >> report.csv
+}
+
+function geomean () {
+    printf "%6.2f ms" $(awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++} END{m=tot/c; printf "%.2f\n", E^m}' $*)
+}
+
+
+
+function do_summary () {
+    echo ===report.csv================================================
+    cat report.csv
+
+    mkdir -p logs/warmup
+    mv logs/*.0.elapsed logs/*.0 logs/warmup
+    local n=0
+    for i in "$@"; do
+        n=$(expr $n + 1)
+        local APPID=$APP-jvm$n
+        report ==============================jvm$n $i
+        if test -f $APPID-mainline.classlist; then
+            report "[$(printf %-6s ${n}_xoff)] Mainline JDK (CDS disabled)                   $(geomean logs/${n}_xoff.*.elapsed)"
+            report "[$(printf %-6s ${n}_xon )] Mainline JDK (CDS enabled)                    $(geomean logs/${n}_xon.*.elapsed)"
+        else
+            report "[$(printf %-6s ${n}_xon )] Premain Prototype (CDS )                      $(geomean logs/${n}_xon.*.elapsed)"
+            report "[$(printf %-6s ${n}_td  )] Premain Prototype (CDS + Training Data)       $(geomean logs/${n}_td.*.elapsed)"
+            report "[$(printf %-6s ${n}_aot )] Premain Prototype (CDS + Training Data + AOT) $(geomean logs/${n}_aot.*.elapsed)"
+        fi
+    done
+}
+
+main "$@"
diff a/test/hotspot/jtreg/premain/lib/build-for-jtreg.sh b/test/hotspot/jtreg/premain/lib/build-for-jtreg.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/lib/build-for-jtreg.sh
@@ -0,0 +1,87 @@
+# Copyright (c) 2024, 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.
+
+
+
+# build-for-jtreg.sh --
+#
+# This script builds the binaries needed by the tests under ../../runtime/cds/appcds/applications/
+# It also prints out the arguments to be added to the jtreg command-line
+
+# Edit the following according to your local setting, or pass them in like this
+#
+# env BLDJDK_HOME=/my/java/home ROOT=/my/repo MVN=${HOME}/bin/maven/apache-maven-3.8.8/bin/mvn bash build-for-jtreg.sh
+#
+if test "$BLDJDK_HOME" = ""; then
+    # you need JDK 21
+    BLDJDK_HOME=/jdk3/official/jdk21
+fi
+
+if test "$ROOT" = ""; then
+    # This is your copy of https://github.com/openjdk/leyden/tree/premain
+    ROOT=/jdk3/le4/open
+fi
+
+# MVN should point to the mvn executable. You need at least 3.8.2
+# If MVN is not specified, we will use the mvn installed in your $PATH
+if test "$MVN" != ""; then
+    MVNARG="MVN=$MVN"
+else
+    MVNARG=
+fi
+
+cd $ROOT/test/hotspot/jtreg/premain
+for i in helidon-quickstart-se micronaut-first-app quarkus-getting-started spring-petclinic; do
+    (cd $i; make app BLDJDK_HOME=${BLDJDK_HOME} ${MVNARG}) || exit 1
+done
+
+PM=$ROOT/test/hotspot/jtreg/premain
+echo Add the following to your jtreg command-line
+echo "  -vmoption:-Djdk.test.lib.artifacts.helidon-quickstart-se=$PM/helidon-quickstart-se/helidon-quickstart-se \\"
+echo "  -vmoption:-Djdk.test.lib.artifacts.micronaut-first-app=$PM/micronaut-first-app/download/target \\" 
+echo "  -vmoption:-Djdk.test.lib.artifacts.quarkus-getting-started=$PM/quarkus-getting-started/getting-started/target \\"
+echo "  -vmoption:-Djdk.test.lib.artifacts.spring-petclinic=$PM/spring-petclinic/petclinic-snapshot/target/spring-petclinic-3.2.0.zip \\"
+
+exit
+
+===============================================================================
+This is how Ioi executes the tests:
+
+env JAVA_HOME=/jdk3/official/jdk22 \
+    /jdk3/official/jtreg7.3.1/bin/jtreg \
+    -J-Djavatest.maxOutputSize=10000000 \
+    -conc:1 \
+    -testjdk:/jdk3/bld/le4-fastdebug/images/jdk \
+    -compilejdk:/jdk3/bld/le4/images/jdk \
+    -verbose:2 \
+    -timeout:4.0 \
+    -agentvm \
+    -vmoptions:-XX:MaxRAMPercentage=6 \
+    -noreport \
+    -vmoption:-Djdk.test.lib.artifacts.helidon-quickstart-se=/jdk3/le4/open/test/hotspot/jtreg/premain/helidon-quickstart-se/helidon-quickstart-se \
+    -vmoption:-Djdk.test.lib.artifacts.micronaut-first-app=/jdk3/le4/open/test/hotspot/jtreg/premain/micronaut-first-app/download/target \
+    -vmoption:-Djdk.test.lib.artifacts.quarkus-getting-started=/jdk3/le4/open/test/hotspot/jtreg/premain/quarkus-getting-started/getting-started/target \
+    -vmoption:-Djdk.test.lib.artifacts.spring-petclinic=/jdk3/le4/open/test/hotspot/jtreg/premain/spring-petclinic/petclinic-snapshot/target/spring-petclinic-3.2.0.zip \
+    -w /home/iklam/tmp/jtreg/work \
+    HelidonQuickStartSE.java#static \
+    MicronautFirstApp.java#static \
+    QuarkusGettingStarted.java#static \
+    spring-petclinic/SpringPetClinicStatic.java
diff a/test/hotspot/jtreg/premain/lib/premain-run.sh b/test/hotspot/jtreg/premain/lib/premain-run.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/lib/premain-run.sh
@@ -0,0 +1,101 @@
+# Copyright (c) 2023, 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.
+
+# Note: the specifics of the test is defined by files such as ../simple/run.sh
+
+# run all tests
+#      bash run.sh $JAVA
+# run a single test
+#      bash run.sh $JAVA 2
+# clean up
+#      make clean
+
+ONLYTEST=$1
+
+TEST_NUM=0
+
+function do_test () {
+    TEST_NUM=$(expr $TEST_NUM + 1)
+
+    echo ========================================
+    echo Test $TEST_NUM $1
+
+    shift
+    if test "$ONLYTEST" != "" -a "$ONLYTEST" != "$TEST_NUM"; then
+        echo "$@"
+        echo "SKIPPED"
+        return 0
+    else
+        local before_errfiles=$(ls hs_err*  2> /dev/null | sort)
+        echo "$@"
+        if eval "$@"; then
+            true
+        else
+            echo "FAILED; command line was:"
+            echo "$@";
+            exit 1
+        fi
+        local after_errfiles=$(ls hs_err*  2> /dev/null | sort)
+        if test "$before_errfiles" != "$after_errfiles"; then
+            echo "HotSpot crashed (check hs_err errfiles); command line was:"
+            echo "$@"
+            exit 1
+        fi
+        echo "PASSED $TEST_NUM"
+        if test "$ONLYTEST" = "$TEST_NUM"; then
+            echo "Exiting after test $TEST_NUM has passed. Further tests may have been skipped"
+            exit 0
+        fi
+    fi
+}
+
+do_test "(STEP 1 of 5) Dump classlist" \
+        $JAVA -Xshare:off -XX:DumpLoadedClassList=$APP.classlist $CMDLINE
+
+if test "$JAR" != ""; then
+    DUMP_CP="-cp"
+else
+    DUMP_CP=
+fi
+
+do_test "(STEP 2 of 5) Create Static $APP-static.jsa" \
+    $JAVA -Xshare:dump -XX:SharedArchiveFile=$APP-static.jsa -XX:SharedClassListFile=$APP.classlist $DUMP_CP $JAR \
+    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=$APP-static.dump.log::filesize=0 $DUMP_EXTRA_ARGS
+
+if false; then
+    do_test "Run with $APP-static.jsa" \
+            $JAVA -XX:SharedArchiveFile=$APP-static.jsa $CMDLINE
+fi
+
+do_test "(STEP 3 of 5) Run with $APP-static.jsa and dump profile in $APP-dynamic.jsa (With Training Data Replay)" \
+    $JAVA -XX:SharedArchiveFile=$APP-static.jsa -XX:ArchiveClassesAtExit=$APP-dynamic.jsa -XX:+RecordTraining \
+        -Xlog:cds=debug,cds+class=debug:file=$APP-dynamic.dump.log::filesize=0 \
+        $CMDLINE
+
+do_test "(STEP 4 of 5) Run with $APP-dynamic.jsa and generate AOT code" \
+    $JAVA -XX:SharedArchiveFile=$APP-dynamic.jsa -XX:+ReplayTraining -XX:+StoreCachedCode \
+        -Xlog:scc*=warning:file=$APP-store-sc.log::filesize=0 \
+        -XX:CachedCodeFile=$APP-dynamic.jsa-sc -XX:CachedCodeMaxSize=100M $CMDLINE
+
+do_test "(STEP 5 of 5) Final production run: with $APP-dynamic.jsa and load AOT code" \
+    $JAVA -XX:SharedArchiveFile=$APP-dynamic.jsa -XX:+ReplayTraining -XX:+LoadCachedCode \
+        -Xlog:scc*=warning:file=$APP-load-sc.log::filesize=0 \
+        -XX:CachedCodeFile=$APP-dynamic.jsa-sc $CMDLINE
diff a/test/hotspot/jtreg/premain/micronaut-first-app/Makefile b/test/hotspot/jtreg/premain/micronaut-first-app/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/Makefile
@@ -0,0 +1,54 @@
+# Copyright (c) 2024, 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.
+
+#===============================================================================
+# Leyden-premain + Micronaut first app
+#  - https://guides.micronaut.io/latest/creating-your-first-micronaut-app-maven-java.html
+#  - https://guides.micronaut.io/latest/creating-your-first-micronaut-app-maven-java.zip
+#
+# See ../lib/DemoSupport.gmk for the "make" targets that build the demo, run the
+# demo, and gather benchmarking data.
+#===============================================================================
+
+# The following needs to be defined before including ../lib/DemoSupport.gmk
+DEMO_NAME       = micronaut-first-app
+DEMO_SRC        = $(wildcard ./download/src/main/java/example/micronaut/*.java)
+DEMO_JAR        = download/target/default-0.1.jar
+DEMO_CMDLINE    = -DautoQuit=true -Dmicronaut.server.port=0 -jar ${DEMO_JAR}
+# This is for uploading into Artifactory, usually to a path like
+#    https://xxxx.com/artifactory/yyyy/io/micronaut/micronaut-first-app/1.0.0/micronaut-first-app-1.0.0.zip
+DEMO_ARTIFACT   = micronaut-first-app-1.0.0.zip
+
+include ../lib/DemoSupport.gmk
+
+${DEMO_JAR}: ${DEMO_SRC}
+	cd download && JAVA_HOME=${BLDJDK_HOME} ${MVN} package
+
+${DEMO_ARTIFACT}: ${DEMO_JAR}
+	${JAR} cvfM ${DEMO_ARTIFACT} \
+		-C download/target default-0.1.jar
+
+clean: clean0
+	rm -rf getting-started ${DEMO_ARTIFACT} download/target
+
+clean0:
+	rm -f *.cds *.cds.code *.classlist *.jsa *~ *.log *.log.* hs_err_*
+
diff a/test/hotspot/jtreg/premain/micronaut-first-app/README.md b/test/hotspot/jtreg/premain/micronaut-first-app/README.md
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/README.md
@@ -0,0 +1,2 @@
+
+./download : This directory is unpacked from https://guides.micronaut.io/latest/creating-your-first-micronaut-app-maven-java.zip , as downloaded on 2024/04/22. See more information at: https://guides.micronaut.io/latest/creating-your-first-micronaut-app-maven-java.html
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/.mvn/wrapper/maven-wrapper.jar b/test/hotspot/jtreg/premain/micronaut-first-app/download/.mvn/wrapper/maven-wrapper.jar
Binary files /dev/null and test/hotspot/jtreg/premain/micronaut-first-app/download/.mvn/wrapper/maven-wrapper.jar differ
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/.mvn/wrapper/maven-wrapper.properties b/test/hotspot/jtreg/premain/micronaut-first-app/download/.mvn/wrapper/maven-wrapper.properties
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/LICENSEHEADER b/test/hotspot/jtreg/premain/micronaut-first-app/download/LICENSEHEADER
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/LICENSEHEADER
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2017-$YEAR original authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/README.md b/test/hotspot/jtreg/premain/micronaut-first-app/download/README.md
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/README.md
@@ -0,0 +1,29 @@
+## Micronaut 4.4.0 Documentation
+
+- [User Guide](https://docs.micronaut.io/4.4.0/guide/index.html)
+- [API Reference](https://docs.micronaut.io/4.4.0/api/index.html)
+- [Configuration Reference](https://docs.micronaut.io/4.4.0/guide/configurationreference.html)
+- [Micronaut Guides](https://guides.micronaut.io/index.html)
+---
+
+- [Micronaut Maven Plugin documentation](https://micronaut-projects.github.io/micronaut-maven-plugin/latest/)
+## Feature maven-enforcer-plugin documentation
+
+- [https://maven.apache.org/enforcer/maven-enforcer-plugin/](https://maven.apache.org/enforcer/maven-enforcer-plugin/)
+
+
+## Feature micronaut-aot documentation
+
+- [Micronaut AOT documentation](https://micronaut-projects.github.io/micronaut-aot/latest/guide/)
+
+
+## Feature serialization-jackson documentation
+
+- [Micronaut Serialization Jackson Core documentation](https://micronaut-projects.github.io/micronaut-serialization/latest/guide/)
+
+
+## Feature spotless documentation
+
+- [https://github.com/diffplug/spotless](https://github.com/diffplug/spotless)
+
+
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/aot-jar.properties b/test/hotspot/jtreg/premain/micronaut-first-app/download/aot-jar.properties
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/aot-jar.properties
@@ -0,0 +1,37 @@
+# AOT configuration properties for jar packaging
+# Please review carefully the optimizations enabled below
+# Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
+
+# Caches environment property values: environment properties will be deemed immutable after application startup.
+cached.environment.enabled=true
+
+# Precomputes Micronaut configuration property keys from the current environment variables
+precompute.environment.properties.enabled=true
+
+# Replaces logback.xml with a pure Java configuration
+logback.xml.to.java.enabled=true
+
+# Converts YAML configuration files to Java configuration
+yaml.to.java.config.enabled=true
+
+# Scans for service types ahead-of-time, avoiding classpath scanning at startup
+serviceloading.jit.enabled=true
+
+# Scans reactive types at build time instead of runtime
+scan.reactive.types.enabled=true
+
+# Deduces the environment at build time instead of runtime
+deduce.environment.enabled=true
+
+# Checks of existence of some types at build time instead of runtime
+known.missing.types.enabled=true
+
+# Precomputes property sources at build time
+sealed.property.source.enabled=true
+
+# The list of service types to be scanned (comma separated)
+service.types=io.micronaut.context.env.PropertySourceLoader,io.micronaut.inject.BeanConfiguration,io.micronaut.inject.BeanDefinitionReference,io.micronaut.http.HttpRequestFactory,io.micronaut.http.HttpResponseFactory,io.micronaut.core.beans.BeanIntrospectionReference,io.micronaut.core.convert.TypeConverterRegistrar,io.micronaut.context.env.PropertyExpressionResolver
+
+# A list of types that the AOT analyzer needs to check for existence (comma separated)
+known.missing.types.list=io.reactivex.Observable,reactor.core.publisher.Flux,kotlinx.coroutines.flow.Flow,io.reactivex.rxjava3.core.Flowable,io.reactivex.rxjava3.core.Observable,io.reactivex.Single,reactor.core.publisher.Mono,io.reactivex.Maybe,io.reactivex.rxjava3.core.Single,io.reactivex.rxjava3.core.Maybe,io.reactivex.Completable,io.reactivex.rxjava3.core.Completable,io.methvin.watchservice.MacOSXListeningWatchService,io.micronaut.core.async.publisher.CompletableFuturePublisher,io.micronaut.core.async.publisher.Publishers.JustPublisher,io.micronaut.core.async.subscriber.Completable
+
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/aot-native-image.properties b/test/hotspot/jtreg/premain/micronaut-first-app/download/aot-native-image.properties
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/aot-native-image.properties
@@ -0,0 +1,40 @@
+# AOT configuration properties for native-image packaging
+# Please review carefully the optimizations enabled below
+# Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details
+
+# Caches environment property values: environment properties will be deemed immutable after application startup.
+cached.environment.enabled=true
+
+# Precomputes Micronaut configuration property keys from the current environment variables
+precompute.environment.properties.enabled=true
+
+# Replaces logback.xml with a pure Java configuration
+logback.xml.to.java.enabled=true
+
+# Converts YAML configuration files to Java configuration
+yaml.to.java.config.enabled=false
+
+# Generates GraalVM configuration files required to load the AOT optimizations
+graalvm.config.enabled=true
+
+# Scans for service types ahead-of-time, avoiding classpath scanning at startup
+serviceloading.native.enabled=false
+
+# Scans reactive types at build time instead of runtime
+scan.reactive.types.enabled=true
+
+# Deduces the environment at build time instead of runtime
+deduce.environment.enabled=true
+
+# Checks of existence of some types at build time instead of runtime
+known.missing.types.enabled=true
+
+# Precomputes property sources at build time
+sealed.property.source.enabled=true
+
+# The list of service types to be scanned (comma separated)
+service.types=io.micronaut.context.env.PropertySourceLoader,io.micronaut.inject.BeanConfiguration,io.micronaut.inject.BeanDefinitionReference,io.micronaut.http.HttpRequestFactory,io.micronaut.http.HttpResponseFactory,io.micronaut.core.beans.BeanIntrospectionReference,io.micronaut.core.convert.TypeConverterRegistrar,io.micronaut.context.env.PropertyExpressionResolver
+
+# A list of types that the AOT analyzer needs to check for existence (comma separated)
+known.missing.types.list=io.reactivex.Observable,reactor.core.publisher.Flux,kotlinx.coroutines.flow.Flow,io.reactivex.rxjava3.core.Flowable,io.reactivex.rxjava3.core.Observable,io.reactivex.Single,reactor.core.publisher.Mono,io.reactivex.Maybe,io.reactivex.rxjava3.core.Single,io.reactivex.rxjava3.core.Maybe,io.reactivex.Completable,io.reactivex.rxjava3.core.Completable,io.methvin.watchservice.MacOSXListeningWatchService,io.micronaut.core.async.publisher.CompletableFuturePublisher,io.micronaut.core.async.publisher.Publishers.JustPublisher,io.micronaut.core.async.subscriber.Completable
+
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/micronaut-cli.yml b/test/hotspot/jtreg/premain/micronaut-first-app/download/micronaut-cli.yml
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/micronaut-cli.yml
@@ -0,0 +1,6 @@
+applicationType: default
+defaultPackage: example.micronaut
+testFramework: junit
+sourceLanguage: java
+buildTool: maven
+features: [app-name, graalvm, http-client-test, java, java-application, junit, logback, maven, maven-enforcer-plugin, micronaut-aot, micronaut-http-validation, netty-server, properties, readme, serialization-jackson, shade, spotless, static-resources]
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/mvnw b/test/hotspot/jtreg/premain/micronaut-first-app/download/mvnw
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/mvnw
@@ -0,0 +1,287 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.1.1
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME
+      else
+        JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`\\unset -f command; \\command -v java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  printf '%s' "$(cd "$basedir"; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname $0)")
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    else
+      wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $wrapperUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        QUIET="--quiet"
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+          QUIET=""
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget $QUIET "$wrapperUrl" -O "$wrapperJarPath"
+        else
+            wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath"
+        fi
+        [ $? -eq 0 ] || rm -f "$wrapperJarPath"
+    elif command -v curl > /dev/null; then
+        QUIET="--silent"
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+          QUIET=""
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L
+        else
+            curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L
+        fi
+        [ $? -eq 0 ] || rm -f "$wrapperJarPath"
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaSource=`cygpath --path --windows "$javaSource"`
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaSource" ]; then
+            if [ ! -e "$javaClass" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaSource")
+            fi
+            if [ -e "$javaClass" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/mvnw.bat b/test/hotspot/jtreg/premain/micronaut-first-app/download/mvnw.bat
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/mvnw.bat
@@ -0,0 +1,187 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.1.1
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %WRAPPER_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/pom.xml b/test/hotspot/jtreg/premain/micronaut-first-app/download/pom.xml
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/pom.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>example.micronaut</groupId>
+  <artifactId>default</artifactId>
+  <version>0.1</version>
+  <packaging>${packaging}</packaging>
+
+  <parent>
+    <groupId>io.micronaut.platform</groupId>
+    <artifactId>micronaut-parent</artifactId>
+    <version>4.4.0</version>
+  </parent>
+  <properties>
+    <packaging>jar</packaging>
+    <jdk.version>17</jdk.version>
+    <release.version>17</release.version>
+    <micronaut.version>4.4.0</micronaut.version>
+    <micronaut.runtime>netty</micronaut.runtime>
+    <micronaut.aot.enabled>false</micronaut.aot.enabled>
+    <micronaut.aot.packageName>example.micronaut.aot.generated</micronaut.aot.packageName>
+    <exec.mainClass>example.micronaut.Application</exec.mainClass>
+  </properties>
+
+  <repositories>
+    <repository>
+      <id>central</id>
+      <url>https://repo.maven.apache.org/maven2</url>
+    </repository>
+  </repositories>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.micronaut</groupId>
+      <artifactId>micronaut-http-server-netty</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.micronaut.serde</groupId>
+      <artifactId>micronaut-serde-jackson</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.micronaut</groupId>
+      <artifactId>micronaut-http-client</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.micronaut.test</groupId>
+      <artifactId>micronaut-test-junit5</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>io.micronaut.maven</groupId>
+        <artifactId>micronaut-maven-plugin</artifactId>
+        <configuration>
+          <configFile>aot-${packaging}.properties</configFile>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>com.diffplug.spotless</groupId>
+        <artifactId>spotless-maven-plugin</artifactId>
+        <version>2.41.1</version>
+        <configuration>
+          <java>
+              <licenseHeader>
+                  <file>${project.basedir}/LICENSEHEADER</file>
+              </licenseHeader>
+          </java>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <!-- Uncomment to enable incremental compilation -->
+          <!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->
+
+          <annotationProcessorPaths combine.children="append">
+            <path>
+              <groupId>io.micronaut</groupId>
+              <artifactId>micronaut-http-validation</artifactId>
+              <version>${micronaut.core.version}</version>
+            </path>
+            <path>
+              <groupId>io.micronaut.serde</groupId>
+              <artifactId>micronaut-serde-processor</artifactId>
+              <version>${micronaut.serialization.version}</version>
+              <exclusions>
+                <exclusion>
+                  <groupId>io.micronaut</groupId>
+                  <artifactId>micronaut-inject</artifactId>
+                </exclusion>
+              </exclusions>
+            </path>
+          </annotationProcessorPaths>
+          <compilerArgs>
+            <arg>-Amicronaut.processing.group=example.micronaut</arg>
+            <arg>-Amicronaut.processing.module=default</arg>
+          </compilerArgs>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/java/example/micronaut/Application.java b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/java/example/micronaut/Application.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/java/example/micronaut/Application.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-2024 original authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.micronaut;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+
+import io.micronaut.runtime.Micronaut;
+
+public class Application {
+
+    public static void main(String[] args) {
+ 	long mainStart = System.currentTimeMillis();
+ 	RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+ 	// This includes all the time spent inside the JVM before main() is reached
+ 	// (since os::Posix::init is called and initial_time_count is initialized).
+ 	long vmStart = runtimeMXBean.getStartTime();
+ 	long maxBeanOverHead = System.currentTimeMillis() - mainStart;
+
+        Micronaut.run(Application.class, args);
+
+        if (Boolean.getBoolean("autoQuit")) {
+            long end = System.currentTimeMillis();
+            System.out.println("#### Booted and returned in " + (end - vmStart - maxBeanOverHead) + "ms");
+            System.out.println("#### (debug) mainStart = " + mainStart);
+            System.out.println("#### (debug) vmStart = " + vmStart);
+            System.out.println("#### (debug) before main (mainStart - vmStart) = " + (mainStart - vmStart));
+            System.out.println("#### (debug) maxBeanOverHead = " + maxBeanOverHead);
+            System.out.println("#### (debug) end = " + end);
+
+            System.out.println("Done .. Exiting");
+            System.exit(0);
+        }
+    }
+}
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/java/example/micronaut/HelloController.java b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/java/example/micronaut/HelloController.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/java/example/micronaut/HelloController.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-2024 original authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.micronaut;
+
+import io.micronaut.http.MediaType;
+import io.micronaut.http.annotation.Controller;
+import io.micronaut.http.annotation.Get;
+import io.micronaut.http.annotation.Produces;
+
+@Controller("/hello") // <1>
+public class HelloController {
+    @Get // <2>
+    @Produces(MediaType.TEXT_PLAIN) // <3>
+    public String index() {
+        return "Hello World"; // <4>
+    }
+}
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/resources/application.properties b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/resources/application.properties
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+#Thu Apr 18 15:55:30 UTC 2024
+micronaut.application.name=default
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/resources/logback.xml b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/resources/logback.xml
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/main/resources/logback.xml
@@ -0,0 +1,14 @@
+<configuration>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!-- encoders are assigned the type
+             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+        <encoder>
+            <pattern>%cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="info">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/src/test/java/example/micronaut/DefaultTest.java b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/test/java/example/micronaut/DefaultTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/test/java/example/micronaut/DefaultTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017-2024 original authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.micronaut;
+
+import io.micronaut.runtime.EmbeddedApplication;
+import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Assertions;
+
+import jakarta.inject.Inject;
+
+@MicronautTest
+class DefaultTest {
+
+    @Inject
+    EmbeddedApplication<?> application;
+
+    @Test
+    void testItWorks() {
+        Assertions.assertTrue(application.isRunning());
+    }
+
+}
diff a/test/hotspot/jtreg/premain/micronaut-first-app/download/src/test/java/example/micronaut/HelloControllerTest.java b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/test/java/example/micronaut/HelloControllerTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/micronaut-first-app/download/src/test/java/example/micronaut/HelloControllerTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-2024 original authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.micronaut;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.micronaut.http.HttpRequest;
+import io.micronaut.http.MediaType;
+import io.micronaut.http.client.HttpClient;
+import io.micronaut.http.client.annotation.Client;
+import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.inject.Inject;
+
+@MicronautTest // <1>
+public class HelloControllerTest {
+
+    @Inject
+    @Client("/")  // <2>
+    HttpClient client;
+
+    @Test
+    public void testHello() {
+        HttpRequest<?> request = HttpRequest.GET("/hello").accept(MediaType.TEXT_PLAIN);  // <3>
+        String body = client.toBlocking().retrieve(request);
+
+        assertNotNull(body);
+        assertEquals("Hello World", body);
+    }
+}
diff a/test/hotspot/jtreg/premain/quarkus-getting-started/Main.java b/test/hotspot/jtreg/premain/quarkus-getting-started/Main.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/quarkus-getting-started/Main.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2024, 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.
+ */
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+
+import io.quarkus.runtime.Quarkus;
+import io.quarkus.runtime.QuarkusApplication;
+import io.quarkus.runtime.annotations.QuarkusMain;
+
+@QuarkusMain
+public class Main {
+    public static void main(String... args) throws Exception {
+ 	long mainStart = System.currentTimeMillis();
+ 	RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+ 	// This includes all the time spent inside the JVM before main() is reached
+ 	// (since os::Posix::init is called and initial_time_count is initialized).
+ 	long vmStart = runtimeMXBean.getStartTime();
+ 	long maxBeanOverHead = System.currentTimeMillis() - mainStart;
+
+        if (Boolean.getBoolean("autoQuit")) {
+            Quarkus.manualInitialize();
+            Quarkus.manualStart();
+
+            long end = System.currentTimeMillis();
+            System.out.println("#### Booted and returned in " + (end - vmStart - maxBeanOverHead) + "ms");
+            System.out.println("#### (debug) mainStart = " + mainStart);
+            System.out.println("#### (debug) vmStart = " + vmStart);
+            System.out.println("#### (debug) before main (mainStart - vmStart) = " + (mainStart - vmStart));
+            System.out.println("#### (debug) maxBeanOverHead = " + maxBeanOverHead);
+            System.out.println("#### (debug) end = " + end);
+
+            System.out.println("Done .. Exiting");
+            System.exit(0);
+        } else {
+            Quarkus.run(MyApp.class, args);
+        }
+    }
+
+    public static class MyApp implements QuarkusApplication {
+        @Override
+        public int run(String... args) throws Exception {
+            System.out.println("Do startup logic here");
+            Quarkus.waitForExit();
+            System.out.println("Done");
+            Thread.sleep(1000);
+            System.out.println("Exiting");
+            Quarkus.asyncExit(0);
+            return 0;
+        }
+    }
+}
diff a/test/hotspot/jtreg/premain/quarkus-getting-started/Makefile b/test/hotspot/jtreg/premain/quarkus-getting-started/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/quarkus-getting-started/Makefile
@@ -0,0 +1,69 @@
+# Copyright (c) 2024, 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.
+
+#===============================================================================
+# Leyden-premain + Quarkus getting-started demo
+#  - https://quarkus.io/guides/getting-started
+#  - https://quarkus.io/guides/lifecycle
+#
+# See ../lib/DemoSupport.gmk for the "make" targets that build the demo, run the
+# demo, and gather benchmarking data.
+#===============================================================================
+
+# The following needs to be defined before including ../lib/DemoSupport.gmk
+DEMO_NAME       = quarkus-getting-started
+DEMO_SRC        = getting-started/src/main/java/Main.java
+DEMO_JAR        = getting-started/target/getting-started-1.0.0-SNAPSHOT-runner.jar
+DEMO_CMDLINE    = -DautoQuit=true -Dquarkus.http.port=0 -jar ${DEMO_JAR}
+# This is for uploading into Artifactory, usually to a path like
+#    https://xxxx.com/artifactory/yyyy/io/quarkus/quarkus-getting-started/1.0.0b/quarkus-getting-started-1.0.0b.zip
+DEMO_ARTIFACT   = quarkus-getting-started-1.0.0b.zip
+
+include ../lib/DemoSupport.gmk
+
+# Create the project using instructions from https://quarkus.io/guides/getting-started, then
+# add the Main class which prints out the timing data and exits.
+${DEMO_SRC}: Main.java
+	rm -rf getting-started
+	JAVA_HOME=${BLDJDK_HOME} ${MVN} \
+	    io.quarkus.platform:quarkus-maven-plugin:3.9.3:create \
+	    -DprojectGroupId=org.acme \
+	    -DprojectArtifactId=getting-started \
+	    -Dextensions='rest'
+	cp Main.java ${DEMO_SRC}
+
+# We package the app in the "legacy-jar" to be CDS friendly
+# See https://quarkus.io/guides/class-loading-reference:
+#  "everything is loaded in the system ClassLoader, so it is a completely flat classpath."
+# See https://www.cheat-sheets.org/saved-copy/quarkus-cheat-sheet.pdf
+${DEMO_JAR}: ${DEMO_SRC}
+	cd getting-started && JAVA_HOME=${BLDJDK_HOME} ${MVN} -Dquarkus.package.type=legacy-jar install
+
+${DEMO_ARTIFACT}: ${DEMO_JAR}
+	${JAR} cvfM ${DEMO_ARTIFACT} \
+		-C getting-started/target .
+
+clean: clean0
+	rm -rf getting-started ${DEMO_ARTIFACT}
+
+clean0:
+	rm -f *.cds *.cds.code *.classlist *.jsa *~ *.log *.log.* hs_err_*
+
diff a/test/hotspot/jtreg/premain/simple/Makefile b/test/hotspot/jtreg/premain/simple/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/simple/Makefile
@@ -0,0 +1,4 @@
+clean:
+	rm -f *.jsa *.jsa-sc *.log* *.classlist *~ Simple.jar tmpclasses
+
+
diff a/test/hotspot/jtreg/premain/simple/Simple.java b/test/hotspot/jtreg/premain/simple/Simple.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/simple/Simple.java
@@ -0,0 +1,49 @@
+public class Simple {
+    public static void main(String args[]) throws Exception {
+        if (args[0].equals("a")) {
+            foo("222");
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        } else if (args[0].equals("b")) {
+            bar("aaa", "222");
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        } else if (args[0].equals("c")) {
+            baz("aaa", 333);
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        } else if (args[0].equals("loopa")) {
+            loopa();
+            loopa();
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        }
+
+        if (args.length > 1 && args[1].equals("load-extra-class")) {
+            // Work around "There is no class to be included in the dynamic archive." problem, where the
+            // dynamic archive is not generated.
+            DummyClass.doit();
+        }
+    }
+
+    static void loopa() {
+        for (int i = 0; i < 100000; i++) {
+            foo("L");
+        }
+    }
+
+    static String x;
+    static void foo(String b) {
+        x = "LIT" + b;
+    }
+    static void bar(String a, String b) {
+        x = a + b;
+    }
+    static void baz(String a, int b) {
+        x = a + b;
+    }
+
+    static class DummyClass {
+        static void doit() {}
+    }
+}
diff a/test/hotspot/jtreg/premain/simple/run.sh b/test/hotspot/jtreg/premain/simple/run.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/simple/run.sh
@@ -0,0 +1,42 @@
+# Copyright (c) 2023, 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.
+
+# See ../lib/premain-run.sh for command-line options
+
+# simple/run.sh
+#
+#    Run a simple test case with a loop. Tests CDS handling of Lambda and AOT
+
+if test ! -f Simple.jar || test Simple.java -nt Simple.jar; then
+    rm -rf tmpclasses
+    mkdir -p tmpclasses
+    javac -d tmpclasses Simple.java || exit
+    jar cf Simple.jar -C tmpclasses . || exit
+    rm -rf tmpclasses
+fi
+
+JAVA=$1; shift 
+JAR=Simple.jar
+CMDLINE="-cp $JAR Simple loopa"
+APP=Simple
+
+source ../lib/premain-run.sh
+
diff a/test/hotspot/jtreg/premain/spring-boot-getting-started/Application.java b/test/hotspot/jtreg/premain/spring-boot-getting-started/Application.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-boot-getting-started/Application.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2024, 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.
+ */
+
+package com.example.springboot;
+
+import java.util.Arrays;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+
+@SpringBootApplication
+public class Application {
+    static long mainStart, vmStart, maxBeanOverHead;
+
+    public static void main(String[] args) {
+ 	mainStart = System.currentTimeMillis();
+ 	RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+ 	// This includes all the time spent inside the JVM before main() is reached
+ 	// (since os::Posix::init is called and initial_time_count is initialized).
+ 	vmStart = runtimeMXBean.getStartTime();
+ 	maxBeanOverHead = System.currentTimeMillis() - mainStart;
+
+        SpringApplication.run(Application.class, args);
+    }
+
+    @Bean
+    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
+       if ("onRefresh".equals(System.getProperty("spring.context.exit"))) {
+            long end = System.currentTimeMillis();
+            System.out.println("#### Booted and returned in " + (end - vmStart - maxBeanOverHead) + "ms");
+            System.out.println("#### (debug) mainStart = " + mainStart);
+            System.out.println("#### (debug) vmStart = " + vmStart);
+            System.out.println("#### (debug) before main (mainStart - vmStart) = " + (mainStart - vmStart));
+            System.out.println("#### (debug) maxBeanOverHead = " + maxBeanOverHead);
+            System.out.println("#### (debug) end = " + end);
+       }
+
+       return args -> {
+           System.out.println("Let's inspect the beans provided by Spring Boot:");
+           String[] beanNames = ctx.getBeanDefinitionNames();
+           Arrays.sort(beanNames);
+           for (String beanName : beanNames) {
+               System.out.println(beanName);
+           }
+       };
+    }
+}
diff a/test/hotspot/jtreg/premain/spring-boot-getting-started/Makefile b/test/hotspot/jtreg/premain/spring-boot-getting-started/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-boot-getting-started/Makefile
@@ -0,0 +1,73 @@
+# Copyright (c) 2024, 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.
+
+#===============================================================================
+# Leyden-premain + Sprint-boot getting-started demo
+#  - https://spring.io/guides/gs/spring-boot
+#  - https://docs.spring.io/spring-boot/maven-plugin/aot.html
+#  - https://bell-sw.com/blog/how-to-use-cds-with-spring-boot-applications/
+#
+# See ../lib/DemoSupport.gmk for the "make" targets that build the demo, run the
+# demo, and gather benchmarking data.
+#===============================================================================
+
+# The following needs to be defined before including ../lib/DemoSupport.gmk
+DEMO_NAME       = spring-boot-getting-started
+DEMO_SRC        = gs-spring-boot/complete/src/main/java/com/example/springboot/Application.java
+DEMO_JAR        = gs-spring-boot/complete/spring-boot-complete-0.0.1-SNAPSHOT/spring-boot-complete-0.0.1-SNAPSHOT.jar
+DEMO_CMDLINE    = -Dserver.port=0 -Dspring.aot.enabled=true -Dspring.output.ansi.enabled=NEVER -Dspring.context.exit=onRefresh -jar ${DEMO_JAR}
+# This is for uploading into Artifactory, usually to a path like
+#    https://xxxx.com/artifactory/yyyy/io/spring/spring-boot-getting-started/3.3.0/spring-boot-getting-started-3.3.0.zip
+DEMO_ARTIFACT   = spring-boot-getting-started-3.3.0.zip
+
+include ../lib/DemoSupport.gmk
+
+# Create the project using instructions from https://spring.io/guides/gs/spring-boot and
+# patch the ${DEMO_SRC} to show start-up measurement used by ../lib/DemoSupport.gmk
+# Note: we use GIT version 99085f078b01fed27667c0b8fbbb957758911f4a
+# Note: pom.xml is patched to add <goal>process-aot</goal>; see https://docs.spring.io/spring-boot/maven-plugin/aot.html
+${DEMO_SRC}: Application.java pom.xml
+	rm -rf gs-spring-boot ${DEMO_SRC}
+	git clone https://github.com/spring-guides/gs-spring-boot.git
+	cd gs-spring-boot; git reset --hard 99085f078b01fed27667c0b8fbbb957758911f4a
+	cp -v Application.java ${DEMO_SRC}
+	cp -v pom.xml gs-spring-boot/complete
+
+# Package the app in CDS friendly way
+# See https://bell-sw.com/blog/how-to-use-cds-with-spring-boot-applications/
+${DEMO_JAR}: ${DEMO_SRC}
+	cd gs-spring-boot/complete && JAVA_HOME=${BLDJDK_HOME} ${MVN} -Dmaven.test.skip=true package
+	cd gs-spring-boot/complete && ${BLDJDK_JAVA} -Djarmode=tools -jar target/spring-boot-complete-0.0.1-SNAPSHOT.jar extract
+
+${DEMO_ARTIFACT}: ${DEMO_JAR}
+	${JAR} cvfM ${DEMO_ARTIFACT} \
+		-C gs-spring-boot/complete/spring-boot-complete-0.0.1-SNAPSHOT .
+
+clean: clean0
+	rm -rf gs-spring-boot ${DEMO_ARTIFACT} mainline_vs_premain.* compare_premain_builds.*
+
+clean0:
+	rm -f *.cds *.cds.code *.classlist *.jsa *~ *.log *.log.* hs_err_* tmp.txt
+
+# Show the patch we made to gs-spring-boot
+diff:
+	cd gs-spring-boot; git diff -w
+
diff a/test/hotspot/jtreg/premain/spring-boot-getting-started/pom.xml b/test/hotspot/jtreg/premain/spring-boot-getting-started/pom.xml
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-boot-getting-started/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>3.3.0</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>com.example</groupId>
+	<artifactId>spring-boot-complete</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>spring-boot-complete</name>
+	<description>Demo project for Spring Boot</description>
+
+	<properties>
+		<java.version>17</java.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+
+		<!-- tag::actuator[] -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-actuator</artifactId>
+		</dependency>
+		<!-- end::actuator[] -->
+
+		<!-- tag::tests[] -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<!-- end::tests[] -->
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+					<executions>
+						<execution>
+							<id>process-aot</id>
+							<goals>
+								<goal>process-aot</goal>
+							</goals>
+						</execution>
+					</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
diff a/test/hotspot/jtreg/premain/spring-petclinic/.gitignore b/test/hotspot/jtreg/premain/spring-petclinic/.gitignore
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/.gitignore
@@ -0,0 +1,10 @@
+petclinic-snapshot
+*.cds
+*.cds.code
+*.jsa
+*.log
+*.log.*
+*.classlist
+*.preimage
+tmp.txt
+
diff a/test/hotspot/jtreg/premain/spring-petclinic/Dummy.java b/test/hotspot/jtreg/premain/spring-petclinic/Dummy.java
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/Dummy.java
@@ -0,0 +1,3 @@
+public class Dummy {
+    public static void main(String args[]) {}
+}
diff a/test/hotspot/jtreg/premain/spring-petclinic/Makefile b/test/hotspot/jtreg/premain/spring-petclinic/Makefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/Makefile
@@ -0,0 +1,430 @@
+# Copyright (c) 2023, 2024, 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.
+
+#===============================================================================
+# Leyden-premain + spring-petclinic demo (https://github.com/spring-projects/spring-petclinic)
+#
+# Build spring-petclinic:
+#    make app
+#
+# Build all leyden optimization artifacts (this is using the older "4 step training run" which will be simplified):
+#    make aot
+#
+# Run with Leyden optimizations
+#    make run
+#
+# Run without Leyden optimizations
+#    make run0
+
+# Set the following to point to youd "Build JDK", your JDK mainline build, and your Leyden JDK build.
+#
+#  *** NOTE: JDK 21 (or 17) is needed to build spring-petclinic-3.2.0-SNAPSHOT.jar
+BLDJDK_HOME   = /jdk3/official/jdk21
+
+# Points to your build with https://github.com/openjdk/jdk
+# For comparison purposes, this build should be the latest version of the mainline that
+# has been merged into https://github.com/openjdk/leyden/tree/premain
+MAINLINE_HOME = /jdk3/bld/rum/images/jdk
+
+# Points to your build with https://github.com/openjdk/leyden/tree/premain
+PREMAIN_HOME = /jdk3/bld/le4/images/jdk
+#===============================================================================
+
+LOG_INIT = -Xlog:init
+
+# Options passed to PREMAIN_JAVA. See compare_premain_builds below
+PM_OPTS =
+
+# Usually there's no need to change the following
+JAR_CMD          = ${BLDJDK_HOME}/bin/jar
+JAVAC_CMD        = ${BLDJDK_HOME}/bin/javac
+BLDJDK_JAVA       = ${BLDJDK_HOME}/bin/java
+MAINLINE_JAVA    = ${MAINLINE_HOME}/bin/java
+PREMAIN_JAVA     = ${PREMAIN_HOME}/bin/java ${PM_OPTS}
+PC_REPO          = petclinic-snapshot
+PC_JAVA_SOURCES = ${PC_REPO}/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
+PC_APP_JAR       = ${PC_REPO}/target/spring-petclinic-3.2.0-SNAPSHOT.jar
+PC_APP_UNPACKED  = ${PC_REPO}/target/unpacked
+
+# This is for uploading to artifactory, to be tested with
+# ../../runtime/cds/appcds/leyden/SpringPetClinic.java
+PC_APP_UNPACKED_ZIP = ${PC_REPO}/target/spring-petclinic-3.2.0.zip
+
+PC_MAIN_CLASS    = org.springframework.samples.petclinic.PetClinicApplication
+
+# Note: you can add specify PM_VER (PreMain Version) for comparing the performance of different builds of premain.
+# See the compare_premain_builds target below
+
+# old workflow
+PC_CLASSLIST     = spring-petclinic$(PM_VER).classlist
+PC_STATIC_JSA    = spring-petclinic$(PM_VER).static.jsa
+PC_DYNAMIC_JSA   = spring-petclinic$(PM_VER).dynamic.jsa
+PC_CACHED_CODE   = spring-petclinic$(PM_VER).code.jsa
+
+PC_STATIC_JSA_8315737  = spring-petclinic$(PM_VER).8315737.static.jsa
+
+# new workflow
+PC_CDS_PREIMAGE  = spring-petclinic$(PM_VER).cds.preimage
+PC_CDS           = spring-petclinic$(PM_VER).cds
+
+# TODO: should we add -Dspring.context.exit=onRefresh to command line??
+# This will make the JVM quit after printing this line:
+#
+#    4:21.639 ... o.s.b.a.e.web.EndpointLinksResolver      : Exposing 13 endpoint(s) beneath base path '/actuator'
+#
+# Whereas -DautoQuit=true will make it exit after printing the following (a little bit of application code is executed)
+#
+#    4:21.665 ... o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ''
+#    4:21.666 ... o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 1.358 seconds (process running for 1.584)
+#    #### Booted and returned in 1453ms
+#    4:21.692 ... j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
+#    4:21.693 ... com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
+#    4:21.694 ... com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
+HEAP_SIZE        = -Xmx2g
+UNPACKED_CMDLINE = $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath -DautoQuit=true -Dspring.aot.enabled=true -Dspring.output.ansi.enabled=NEVER ${PC_MAIN_CLASS}
+DUMMY_CMDLINE    = $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath.dummy Dummy
+
+all: check git app aot
+
+check:
+	@if ${PREMAIN_JAVA} -XX:+PrintFlagsFinal --version | grep -q CDSManualFinalImage; then \
+	    true; \
+	else \
+	    echo PREMAIN_HOME should point to your build of https://github.com/openjdk/leyden/tree/premain; \
+	    exit 1; \
+	fi
+
+# A bunch of quick targets so you can type "make list" to create the classlist, etc
+git: ${PC_JAVA_SOURCES}
+app: ${PC_APP_JAR}
+unpack: ${PC_APP_UNPACKED}
+
+# The "4 step training run" workflow -- this will soon be replaced with the new workflow in ../javac_new_workflow/
+list: ${PC_CLASSLIST}
+static: ${PC_STATIC_JSA}
+dynamic: ${PC_DYNAMIC_JSA}
+aot: ${PC_CACHED_CODE}
+
+# Check out this specific version of spring-petclinic that we have been testing with.
+# https://github.com/spring-projects/spring-petclinic/commit/80fd11067c4662486e4c635deceba927375b621c
+#    Author: Dave Syer <dsyer@vmware.com>
+#    Date:   Wed Jan 10 05:01:00 2024
+#        Upgrade to Boot 3.2.1
+${PC_JAVA_SOURCES}:
+	rm -rf ${PC_REPO}
+	git clone https://github.com/spring-projects/spring-petclinic ${PC_REPO}
+	cd ${PC_REPO}; git reset --hard 80fd11067c4662486e4c635deceba927375b621c
+	cd ${PC_REPO}; git apply ../premain-patch.diff
+
+# Need to touch ${PC_APP_JAR} as mvn wants to set it the release date of 3.2.0-SNAPSHOT
+${PC_APP_JAR}: ${PC_JAVA_SOURCES}
+	cd ${PC_REPO}; JAVA_HOME=${BLDJDK_HOME} mvn -Dmaven.test.skip=true package
+	if test -f ${PC_APP_JAR}; then \
+	    touch ${PC_APP_JAR}; \
+	fi
+
+${PC_APP_UNPACKED}: ${PC_APP_JAR}
+	rm -rf ${PC_APP_UNPACKED}
+	mkdir -p ${PC_APP_UNPACKED}
+	cd ${PC_APP_UNPACKED} && \
+	    ${JAR_CMD} xf ../spring-petclinic-3.2.0-SNAPSHOT.jar && \
+	    ${JAR_CMD} cf classes.jar META-INF org && \
+            cd BOOT-INF/classes/ && \
+	    ${JAR_CMD} cf classes.jar *
+	echo ${PC_APP_UNPACKED}/classes.jar $$(find ${PC_APP_UNPACKED}/BOOT-INF -name \*.jar | sort) \
+	    | sed -e 's/ /:/g' > ${PC_APP_UNPACKED}/classpath
+	echo $$(cat ${PC_APP_UNPACKED}/classpath):Dummy.jar > ${PC_APP_UNPACKED}/classpath.dummy
+	${JAR_CMD} cf0 ${PC_APP_UNPACKED_ZIP} ${PC_APP_UNPACKED}
+	$(MAKE) runb
+
+#uncomment this to see the generated classes for dynamic proxies
+#SAVE_GEN_FILES=-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
+
+${PC_CLASSLIST}: ${PC_APP_UNPACKED}
+	${PREMAIN_JAVA} -Xshare:off -XX:DumpLoadedClassList=${PC_CLASSLIST} \
+	    -Xlog:class+load=debug:file=${PC_CLASSLIST}.classload.log ${SAVE_GEN_FILES} \
+	    ${UNPACKED_CMDLINE}
+	wc -lc ${PC_CLASSLIST}
+
+${PC_STATIC_JSA}: ${PC_CLASSLIST}
+	rm -f ${PC_STATIC_JSA}.log
+	${PREMAIN_JAVA} -Xshare:dump -XX:SharedClassListFile=${PC_CLASSLIST} $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath \
+	    -XX:SharedArchiveFile=${PC_STATIC_JSA} \
+	    -XX:+AOTClassLinking -XX:+ArchiveDynamicProxies -XX:+ArchiveReflectionData \
+	    -XX:+ArchiveLoaderLookupCache -Dcds.debug.archived.packages=1 \
+	    -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=${PC_STATIC_JSA}.log
+	ls -l ${PC_STATIC_JSA}
+
+${PC_STATIC_JSA_8315737}: ${PC_CLASSLIST}
+	rm -f ${PC_STATIC_JSA}.log
+	${PREMAIN_JAVA} -Xshare:dump -XX:SharedClassListFile=${PC_CLASSLIST} $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath \
+	    -XX:SharedArchiveFile=${PC_STATIC_JSA_8315737} \
+	    -XX:+AOTClassLinking -XX:-ArchiveDynamicProxies -XX:-ArchiveReflectionData \
+	    -XX:-ArchiveLoaderLookupCache -XX:-ArchiveProtectionDomains -XX:-ArchivePackages \
+	    -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=${PC_STATIC_JSA_8315737}.log
+	ls -l ${PC_STATIC_JSA_8315737}
+
+${PC_DYNAMIC_JSA}: ${PC_STATIC_JSA}
+	rm -f ${PC_DYNAMIC_JSA} ${PC_DYNAMIC_JSA}.log
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} -XX:ArchiveClassesAtExit=${PC_DYNAMIC_JSA} \
+	    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=${PC_DYNAMIC_JSA}.log \
+	    -XX:+RecordTraining ${UNPACKED_CMDLINE}
+	ls -l ${PC_DYNAMIC_JSA}
+
+${PC_CACHED_CODE}: ${PC_DYNAMIC_JSA}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_JSA} -XX:+ReplayTraining -XX:+StoreCachedCode \
+	     -XX:CachedCodeFile=${PC_CACHED_CODE} -XX:CachedCodeMaxSize=512M ${UNPACKED_CMDLINE}
+	ls -l ${PC_CACHED_CODE}
+
+# run with premain optimizatipn
+run: ${PC_CACHED_CODE}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_JSA} -XX:+ReplayTraining -XX:+LoadCachedCode \
+		-XX:CachedCodeFile=${PC_CACHED_CODE} ${LOG_INIT} -Xlog:scc=error ${UNPACKED_CMDLINE}
+
+# run with just static CDS archive
+runs: ${PC_STATIC_JSA}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} ${LOG_INIT} ${UNPACKED_CMDLINE}
+
+# run with just static CDS archive
+runs.8315737: ${PC_STATIC_JSA_8315737}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA_8315737} ${LOG_INIT} ${UNPACKED_CMDLINE}
+
+runs.jep: runs.8315737
+
+# run with just static CDS archive, but instead of the main class, give a fake class name
+# Because -XX:+LoadSharedClasses is specified, this command will basically test the speed of loading all
+# the classes and entering the main() method.
+dummy: ${PC_STATIC_JSA} Dummy.jar
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} ${LOG_INIT} ${DUMMY_CMDLINE}
+
+dummy-50: ${PC_STATIC_JSA} Dummy.jar
+	bash -c "time for i in {1..50}; do ${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} ${DUMMY_CMDLINE}; done"
+
+dummy-50-perf: ${PC_STATIC_JSA} Dummy.jar
+	perf stat -r 50 ${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} ${DUMMY_CMDLINE}
+
+Dummy.jar: Dummy.java
+	${JAVAC_CMD} Dummy.java
+	${JAR_CMD} cf Dummy.jar Dummy*.class
+
+# run with just dynamic CDS archive
+rund: ${PC_DYNAMIC_JSA}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_JSA} \
+		${LOG_INIT} ${UNPACKED_CMDLINE}
+
+# run WITHOUT premain optimizatipn
+run0: ${PC_APP_UNPACKED}
+	${PREMAIN_JAVA} ${LOG_INIT} ${UNPACKED_CMDLINE}
+
+# Run using "build JDK"
+runb: ${PC_APP_UNPACKED}
+	${BLDJDK_JAVA} ${UNPACKED_CMDLINE}
+
+# Running with mainline
+PC_CLASSLIST_MAINLINE   = spring-petclinic.ml.classlist
+PC_STATIC_JSA_MAINLINE  = spring-petclinic.static.ml.jsa
+
+list.ml:   ${PC_CLASSLIST_MAINLINE}
+
+static.ml: ${PC_STATIC_JSA_MAINLINE}
+
+${PC_CLASSLIST_MAINLINE}: ${PC_APP_UNPACKED}
+	${MAINLINE_JAVA} -Xshare:off -XX:DumpLoadedClassList=${PC_CLASSLIST_MAINLINE} \
+	    -Xlog:class+load=debug:file=spring-petclinic.classload.ml.log \
+	    ${UNPACKED_CMDLINE}
+	wc -lc ${PC_CLASSLIST_MAINLINE}
+
+${PC_STATIC_JSA_MAINLINE}: ${PC_CLASSLIST_MAINLINE}
+	rm -f ${PC_STATIC_JSA_MAINLINE}.log
+	${MAINLINE_JAVA} -Xshare:dump -XX:SharedClassListFile=${PC_CLASSLIST_MAINLINE} $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath \
+	    -XX:SharedArchiveFile=${PC_STATIC_JSA_MAINLINE} \
+	    -Xlog:cds=debug,cds+class=debug,cds+heap=warning:file=${PC_STATIC_JSA_MAINLINE}.log
+	ls -l ${PC_STATIC_JSA_MAINLINE}
+
+run0.ml: ${PC_STATIC_JSA_MAINLINE}
+	${MAINLINE_JAVA} \
+		${UNPACKED_CMDLINE}
+
+runs.ml: ${PC_STATIC_JSA_MAINLINE}
+	${MAINLINE_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA_MAINLINE} \
+		${UNPACKED_CMDLINE}
+
+clean.ml:
+	rm -fv ${PC_CLASSLIST_MAINLINE}* ${PC_STATIC_JSA_MAINLINE}*
+
+#======================================== new workflow
+pre: ${PC_CDS_PREIMAGE}
+cds: ${PC_CDS}
+
+${PC_CDS_PREIMAGE}: ${PC_APP_UNPACKED}
+	rm -f ${PC_CDS_PREIMAGE} ${PC_CDS}
+	${PREMAIN_JAVA} -XX:+AOTClassLinking \
+	    -XX:+ArchiveReflectionData \
+	    -XX:+ArchiveDynamicProxies -XX:+ArchiveLoaderLookupCache -XX:CachedCodeMaxSize=512M \
+	    -XX:+UnlockDiagnosticVMOptions -XX:+CDSManualFinalImage -XX:CacheDataStore=${PC_CDS} \
+	    -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=${PC_CDS_PREIMAGE}.log \
+	    ${UNPACKED_CMDLINE}
+	ls -l ${PC_CDS_PREIMAGE}
+
+${PC_CDS}: ${PC_CDS_PREIMAGE}
+	rm -f ${PC_CDS}
+	${PREMAIN_JAVA} -XX:+AOTClassLinking -XX:CachedCodeMaxSize=512M \
+	    -XX:+UnlockDiagnosticVMOptions -XX:CDSPreimage=${PC_CDS_PREIMAGE} -XX:CacheDataStore=${PC_CDS} \
+	    -Xlog:scc,cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=${PC_CDS}.log \
+	    -Dcds.debug.archived.packages=1 \
+	    ${UNPACKED_CMDLINE}
+	ls -l ${PC_CDS}
+	ls -l ${PC_CDS}.code
+
+# run with NEW workflow
+runn: ${PC_CDS}
+	${PREMAIN_JAVA} -XX:CacheDataStore=${PC_CDS} ${LOG_INIT} -Xlog:scc=error ${UNPACKED_CMDLINE}
+
+# see dummy above
+dummyn: ${PC_CDS} Dummy.jar
+	${PREMAIN_JAVA} -XX:CacheDataStore=${PC_CDS} ${LOG_INIT} -Xlog:scc=error ${DUMMY_CMDLINE}
+
+dummyn-50: ${PC_CDS} Dummy.jar
+	bash -c "time for i in {1..50}; do ${PREMAIN_JAVA} -XX:CacheDataStore=${PC_CDS} \
+		-XX:-ReplayTraining -XX:-LoadCachedCode -Xlog:scc=error ${DUMMY_CMDLINE}; done"
+
+dummyn-50-perf: ${PC_CDS} Dummy.jar
+	perf stat -r 50 ${PREMAIN_JAVA} -XX:CacheDataStore=${PC_CDS} -XX:-ReplayTraining -XX:-LoadCachedCode -Xlog:scc=error ${DUMMY_CMDLINE}
+
+clean: clean0
+	rm -rf *~ ${PC_REPO} Dummy*.class Dummy.jar *.old.txt *.new.txt tmp.txt hs_err*
+
+# clean the the leyden artifacts
+clean0:
+	rm -fv spring-petclinic.*
+
+# clean the the leyden artifacts for the new workflow only
+cleann:
+	rm -fv spring-petclinic.cds.*
+
+include ../lib/Bench.gmk
+
+# Same as mainline_vs_premain in ../lib/Bench.gmk, but also includes old workflow
+mainline_vs_premain_all: clean0 _mainline_vs_premain_all
+
+_mainline_vs_premain_all:
+	@echo "run,mainline default,mainline custom static CDS,premain custom static CDS,premain custom static CDS+training+AOT,premain new workflow"
+	@first=1; for i in ${LOOPS}; do \
+		rm -f tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= run0.ml | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runs.ml | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runs    | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= run     | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runn    | grep Booted >> tmp.txt; \
+		if test "$$first" != "1"; then \
+		    echo $$i$$(cat tmp.txt | sed -e 's/.*in /,/g' -e 's/ms//g') | sed -e 's/ //g'; \
+	        fi; \
+		first=0; \
+	done
+
+# Compare
+# (1) Mainline JDK with default CDS archive only
+# (2) Mainline JDK with a custom (static) CDS archive for PetClinic
+# (3) Premain with a custom (static) CDS archive for PetClinic, with only the optimizations in https://openjdk.org/jeps/8315737
+mainline_vs_premain_jep_8315737: clean0 _mainline_vs_premain_jep_8315737
+
+_mainline_vs_premain_jep_8315737:
+	rm -f mainline_vs_premain_jep_8315737.csv
+	@echo "run,mainline default,mainline custom static CDS,premain custom static CDS (JEP 8315737 only),premain custom static CDS (all optimizations),premain all optimizations (incl profiles and AOT)" | \
+	    tee -a mainline_vs_premain_jep_8315737.csv
+	@first=1; for i in ${LOOPS}; do \
+		rm -f tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= run0.ml      | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runs.ml      | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runs.8315737 | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runs         | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= runn         | grep Booted >> tmp.txt; \
+		if test "$$first" != "1"; then \
+		    echo $$i$$(cat tmp.txt | sed -e 's/.*in /,/g' -e 's/ms//g') | sed -e 's/ //g' | tee -a mainline_vs_premain_jep_8315737.csv; \
+	        fi; \
+		first=0; \
+	done
+	@${MAINLINE_JAVA} ../lib/GithubMDChart.java mainline_vs_premain_jep_8315737.md < mainline_vs_premain_jep_8315737.csv
+
+# Same as compare_premain_builds in ../lib/Bench.gmk, but compare CDS only, and old workflow as well
+
+compare_premain_builds_full: clean0 _compare_premain_builds_full
+
+_compare_premain_builds_full:
+	@echo Old build = ${PM_OLD} with options ${PM_OPTS_OLD}
+	@echo New build = ${PM_NEW} with options ${PM_OPTS_NEW}
+	@echo "Run,Old static CDS only,New static CDS only,Old static CDS + training + AOT,New static CDS + training + AOT,Old 1-step training,New 1-step training"
+	@first=1; for i in ${LOOPS}; do \
+		rm -f tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_OLD} PM_VER=.old PM_OPTS="${PM_OPTS_OLD}" runs | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_NEW} PM_VER=.new PM_OPTS="${PM_OPTS_NEW}" runs | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_OLD} PM_VER=.old PM_OPTS="${PM_OPTS_OLD}" run  | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_NEW} PM_VER=.new PM_OPTS="${PM_OPTS_NEW}" run  | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_OLD} PM_VER=.old PM_OPTS="${PM_OPTS_OLD}" runn | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_NEW} PM_VER=.new PM_OPTS="${PM_OPTS_NEW}" runn | grep Booted >> tmp.txt; \
+		if test "$$first" != "1"; then \
+		    echo $$i$$(cat tmp.txt | sed -e 's/.*in /,/g' -e 's/ms//g') | sed -e 's/ //g'; \
+	        fi; \
+		first=0; \
+	done
+
+# same as above, but only compare the speed of "make runs"
+compare_premain_builds_static: clean0 _compare_premain_builds_static
+
+_compare_premain_builds_static:
+	@echo Old build = ${PM_OLD} with options ${PM_OPTS_OLD}
+	@echo New build = ${PM_NEW} with options ${PM_OPTS_NEW}
+	@echo "Run,Old static CDS only,New static CDS only"
+	@first=1; for i in ${LOOPS}; do \
+		rm -f tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_OLD} PM_VER=.old PM_OPTS="${PM_OPTS_OLD}" runs | grep Booted >> tmp.txt; \
+		$(SLEEP) $(MAKE) LOG_INIT= PREMAIN_HOME=${PM_NEW} PM_VER=.new PM_OPTS="${PM_OPTS_NEW}" runs | grep Booted >> tmp.txt; \
+		if test "$$first" != "1"; then \
+		    echo $$i$$(cat tmp.txt | sed -e 's/.*in /,/g' -e 's/ms//g') | sed -e 's/ //g'; \
+	        fi; \
+		first=0; \
+	done
+
+# Clean up the logs generated by compare_premain_builds, so you can see if one build includes
+# more classes than another.
+diff_training_logs:
+	@for i in old new; do \
+	    grep cds,class spring-petclinic.$$i.static.jsa.log | \
+		sed -e 's/.*0x[0-9a-f]* ..... //g' \
+		    -e 's/Lambda\/0x[0-9a-f]*/Lambda\/nnn/g' \
+		    -e 's/MH\/0x[0-9a-f]*/MH\/nnn/g' | \
+		sort > old-workflow-static-classes.$$i.txt; \
+	    grep cds,class spring-petclinic.$$i.dynamic.jsa.log | \
+		sed -e 's/.*0x[0-9a-f]* ..... //g' \
+		    -e 's/Lambda\/0x[0-9a-f]*/Lambda\/nnn/g' \
+		    -e 's/MH\/0x[0-9a-f]*/MH\/nnn/g' | \
+		sort > old-workflow-dynamic-classes.$$i.txt; \
+	    grep cds,class spring-petclinic.$$i.cds.log | \
+		sed -e 's/.*0x[0-9a-f]* ..... //g' \
+		    -e 's/Lambda\/0x[0-9a-f]*/Lambda\/nnn/g' \
+		    -e 's/MH\/0x[0-9a-f]*/MH\/nnn/g' | \
+		sort > new-workflow-classes.$$i.txt; \
+	done
+	@ls -l *.old.txt *.new.txt
+	@echo '***'
+	@echo "You can diff the above files to diagnose speed differences between two builds"
+
+
diff a/test/hotspot/jtreg/premain/spring-petclinic/README.md b/test/hotspot/jtreg/premain/spring-petclinic/README.md
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/README.md
@@ -0,0 +1,14 @@
+# Leyden-premain + spring-petclinic demo
+
+Please see [Makefile](Makefile) for details.
+
+```
+make app
+make aot
+make run0
+make run
+```
+
+Note:
+
+- spring-petclinic is downloaded from https://github.com/spring-projects/spring-petclinic
diff a/test/hotspot/jtreg/premain/spring-petclinic/WarmupMakefile b/test/hotspot/jtreg/premain/spring-petclinic/WarmupMakefile
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/WarmupMakefile
@@ -0,0 +1,384 @@
+# Copyright (c) 2024, 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.
+
+#===============================================================================
+# Leyden-premain + spring-petclinic warmup (https://github.com/spring-projects/spring-petclinic)
+#
+# Build spring-petclinic:
+#    make app
+#
+# Build all leyden optimization artifacts (this is using the older "4 step training run" which will be simplified):
+#    make aot
+#
+# Uses JMeter to drive the load to the petclinic application.
+# Duration of the load and number of threads used by JMeter to drive the load
+# is controlled by JMETER_DURATION and JMETER_THREADS variables respectively.
+# JMETER_CPUS and APP_CPUS variables control the cpus to be used for running the jmeter and app respectively.
+#
+# Run warmup with baseline
+#    make -f WarmupMakefile warmup-bl
+#
+# Run warmup with only preload optimizations
+#    make -f WarmupMakefile warmup-preload
+#
+# Run warmup with training data generated for the startup
+#    make -f WarmupMakefile warmup-st
+#
+# Run warmup with training data generated by the jmeter load
+#    make -f WarmupMakefile warmup-lt
+#
+# Run with startup training data and aot
+#    make -f WarmupMakefile warmup-st-aot
+#
+# Run with startup training data and aot
+#    make -f WarmupMakefile warmup-lt-aot
+#
+# Run all configurations
+#    make -f WarmupMakefile run
+#
+# Use warmup_result.sh to process the generated jmeter log files to create CSV files
+# that can be used for plotting throughput graphs
+#
+# Set the following to point to JDK 17 and your Leyden JDK build.
+#
+#  *** NOTE: JDK 17 (or 21??) is needed to build spring-petclinic-3.2.0-SNAPSHOT.jar
+JDK17_HOME   = /jdk3/official/jdk17
+#baseline java
+BL_HOME      = /jdk3/official/jdk-latest
+PREMAIN_HOME = /jdk3/bld/le4/images/jdk
+#===============================================================================
+
+# Usually there's no need to change the following
+JAR_CMD          = ${JDK17_HOME}/bin/jar
+JDK17_JAVA       = ${JDK17_HOME}/bin/java
+PREMAIN_JAVA     = ${PREMAIN_HOME}/bin/java
+BL_JAVA          = ${BL_HOME}/bin/java
+PC_REPO          = petclinic-snapshot-warmup
+PC_JAVA_SOURCES = ${PC_REPO}/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
+PC_APP_JAR       = ${PC_REPO}/target/spring-petclinic-3.2.0-SNAPSHOT.jar
+PC_APP_UNPACKED  = ${PC_REPO}/target/unpacked
+JMETER_DIR       = apache-jmeter-5.5
+JMETER_BIN       = ${JMETER_DIR}/bin/jmeter
+JMETER_ZIP       = ${JMETER_DIR}.zip
+JMETER_URL       = https://archive.apache.org/dist/jmeter/binaries/${JMETER_ZIP}
+
+# This is for uploading to artifactory, to be tested with
+# ../../runtime/cds/appcds/leyden/SpringPetClinic.java
+PC_APP_UNPACKED_ZIP = ${PC_REPO}/target/spring-petclinic-3.2.0.zip
+
+PC_MAIN_CLASS    = org.springframework.samples.petclinic.PetClinicApplication
+PC_CLASSLIST     = spring-petclinic.classlist
+PC_STATIC_JSA    = spring-petclinic.static.jsa
+PC_DYNAMIC_ST_JSA   = spring-petclinic.dynamic-st.jsa
+PC_DYNAMIC_LT_JSA   = spring-petclinic.dynamic-lt.jsa
+PC_DYNAMIC_NOTRAINING_JSA = spring-petclinic.dynamic-notraining.jsa
+PC_ST_CACHED_CODE   = spring-petclinic.code-st.jsa
+PC_LT_CACHED_CODE   = spring-petclinic.code-lt.jsa
+
+PC_BL_CLASSLIST     = spring-petclinic.bl_classlist
+PC_STATIC_BL_JSA    = spring-petclinic.static_bl.jsa
+PC_DYNAMIC_BL_JSA   = spring-petclinic.dynamic_bl.jsa
+
+PC_CDS_PREIMAGE     = spring-petclinic.cds.preimage
+PC_CDS              = spring-petclinic.cds
+
+JMETER_DURATION = 180
+JMETER_THREADS = 1
+JMETER_CPUS = 12-13
+APP_CPUS = 14-19
+
+# TODO: should we add -Dspring.context.exit=onRefresh to command line??
+# This will make the JVM quit after printing this line:
+#
+#    4:21.639 ... o.s.b.a.e.web.EndpointLinksResolver      : Exposing 13 endpoint(s) beneath base path '/actuator'
+#
+# Whereas -DautoQuit=true will make it exit after printing the following (a little bit of application code is executed)
+#
+#    4:21.665 ... o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ''
+#    4:21.666 ... o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 1.358 seconds (process running for 1.584)
+#    #### Booted and returned in 1453ms
+#    4:21.692 ... j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
+#    4:21.693 ... com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
+#    4:21.694 ... com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
+HEAP_SIZE        = -Xmx2g
+UNPACKED_CMDLINE = $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath -DautoQuit=true -Dspring.aot.enabled=true -Dspring.output.ansi.enabled=NEVER ${PC_MAIN_CLASS}
+
+all: check git app aot
+
+check:
+	@if ${JDK17_JAVA} --version | grep -q '17.*LTS'; then \
+	    true; \
+	else \
+	    echo JDK17_HOME should point to JDK 17; \
+	    exit 1; \
+	fi
+	@if ${PREMAIN_JAVA} -XX:+PrintFlagsFinal --version | grep -q CDSManualFinalImage; then \
+	    true; \
+	else \
+	    echo PREMAIN_HOME should point to your build of https://github.com/openjdk/leyden/tree/premain; \
+	    exit 1; \
+	fi
+
+# A bunch of quick targets so you can type "make list" to create the classlist, etc
+git: ${PC_JAVA_SOURCES}
+app: ${PC_APP_JAR}
+jmeter: ${JMETER_BIN}
+unpack: ${PC_APP_UNPACKED}
+
+# The "4 step training run" workflow -- this will soon be replaced with the new workflow in ../javac_new_workflow/
+list: ${PC_CLASSLIST}
+static: ${PC_STATIC_JSA}
+dynamic: ${PC_DYNAMIC_ST_JSA}
+aot: ${PC_ST_CACHED_CODE}
+
+# Check out this specific version of spring-petclinic that we have been testing with.
+# https://github.com/spring-projects/spring-petclinic/commit/80fd11067c4662486e4c635deceba927375b621c
+#    Author: Dave Syer <dsyer@vmware.com>
+#    Date:   Wed Jan 10 05:01:00 2024
+#        Upgrade to Boot 3.2.1
+${PC_JAVA_SOURCES}:
+	rm -rf ${PC_REPO}
+	git clone https://github.com/spring-projects/spring-petclinic ${PC_REPO}
+	cd ${PC_REPO}; git reset --hard 80fd11067c4662486e4c635deceba927375b621c
+	cd ${PC_REPO}; git apply ../premain-patch-noautoquit.diff
+
+${JMETER_BIN}:
+	rm -fr ${JMETER_DIR}
+	wget ${JMETER_URL}
+	unzip -q ${JMETER_ZIP}
+	ls -l $@
+
+# Need to touch ${PC_APP_JAR} as mvn wants to set it the release date of 3.2.0-SNAPSHOT
+${PC_APP_JAR}: ${PC_JAVA_SOURCES}
+	cd ${PC_REPO}; JAVA_HOME=${JDK17_HOME} mvn -Dmaven.test.skip=true package
+	if test -f ${PC_APP_JAR}; then \
+	    touch ${PC_APP_JAR}; \
+	fi
+
+${PC_APP_UNPACKED}: ${PC_APP_JAR}
+	rm -rf ${PC_APP_UNPACKED}
+	mkdir -p ${PC_APP_UNPACKED}
+	cd ${PC_APP_UNPACKED} && \
+	    ${JAR_CMD} xf ../spring-petclinic-3.2.0-SNAPSHOT.jar && \
+	    ${JAR_CMD} cf classes.jar META-INF org && \
+            cd BOOT-INF/classes/ && \
+	    ${JAR_CMD} cf classes.jar *
+	echo ${PC_APP_UNPACKED}/classes.jar $$(find ${PC_APP_UNPACKED}/BOOT-INF -name \*.jar | sort) \
+	    | sed -e 's/ /:/g' > ${PC_APP_UNPACKED}/classpath
+	${JAR_CMD} cf0 ${PC_APP_UNPACKED_ZIP} ${PC_APP_UNPACKED}
+	$(MAKE) -f WarmupMakefile run17
+
+#uncomment this to see the generated classes for dynamic proxies
+#SAVE_GEN_FILES=-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
+
+define wait-for-startup
+tail -n+0 -f $@.out | sed --quiet '/Booted and returned/ q'
+endef
+
+define exit-app
+cp application.pid app.pid
+curl -X POST localhost:8080/actuator/shutdown
+tail -f --pid=`cat app.pid` /dev/null
+endef
+
+# petclinic_test_plan.jmx is derived from https://github.com/spring-projects/spring-petclinic/blob/main/src/test/jmeter/petclinic_test_plan.jmx
+define run-jmeter
+taskset -c "${JMETER_CPUS}" ${JMETER_BIN} -JDURATION=${JMETER_DURATION} -JTHREADS=${JMETER_THREADS} -Dsummariser.interval=6 -n -t petclinic_test_plan.jmx | tee tput-$@.log
+endef
+
+${PC_BL_CLASSLIST}: ${PC_APP_UNPACKED}
+	${BL_JAVA} -Xshare:off -XX:DumpLoadedClassList=${PC_BL_CLASSLIST} \
+	    -Xlog:class+load=debug:file=$@.log ${SAVE_GEN_FILES} \
+	    ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(exit-app)
+	wc -lc $@
+
+${PC_CLASSLIST}: ${PC_APP_UNPACKED}
+	${PREMAIN_JAVA} -Xshare:off -XX:DumpLoadedClassList=${PC_CLASSLIST} \
+	    -Xlog:class+load=debug:file=$@.log ${SAVE_GEN_FILES} \
+	    ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+	wc -lc $@
+
+${PC_STATIC_BL_JSA}: ${PC_BL_CLASSLIST}
+	rm -f ${PC_STATIC_BL_JSA}.log
+	${BL_JAVA} -Xshare:dump -XX:SharedClassListFile=${PC_BL_CLASSLIST} $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath \
+	    -XX:SharedArchiveFile=${PC_STATIC_BL_JSA} -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=$@.log
+	ls -l $@
+
+${PC_STATIC_JSA}: ${PC_CLASSLIST}
+	rm -f ${PC_STATIC_JSA}.log
+	${PREMAIN_JAVA} -Xshare:dump -XX:SharedClassListFile=${PC_CLASSLIST} $(HEAP_SIZE) -cp @${PC_APP_UNPACKED}/classpath \
+	    -XX:+AOTClassLinking -XX:+ArchiveDynamicProxies -XX:+ArchiveReflectionData \
+            -XX:+ArchiveLoaderLookupCache -Dcds.debug.archived.packages=1 \
+	    -XX:SharedArchiveFile=${PC_STATIC_JSA} -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=$@.log
+	ls -l $@
+
+${PC_DYNAMIC_BL_JSA}: ${PC_STATIC_BL_JSA}
+	rm -f ${PC_DYNAMIC_BL_JSA} ${PC_DYNAMIC_BL_JSA}.log
+	${BL_JAVA} -XX:SharedArchiveFile=${PC_STATIC_BL_JSA} -XX:ArchiveClassesAtExit=${PC_DYNAMIC_BL_JSA} \
+	    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=$@.log \
+	    ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(exit-app)
+	ls -l $@
+
+${PC_DYNAMIC_NOTRAINING_JSA}: ${PC_STATIC_JSA}
+	rm -f ${PC_DYNAMIC_NOTRAINING_JSA} ${PC_DYNAMIC_NOTRAINING_JSA}.log
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} -XX:ArchiveClassesAtExit=${PC_DYNAMIC_NOTRAINING_JSA} \
+	    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=$@.log \
+	    ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(exit-app)
+	ls -l $@
+
+${PC_DYNAMIC_ST_JSA}: ${PC_STATIC_JSA}
+	rm -f ${PC_DYNAMIC_ST_JSA} ${PC_DYNAMIC_ST_JSA}.log
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} -XX:ArchiveClassesAtExit=${PC_DYNAMIC_ST_JSA} \
+	    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=$@.log \
+	    -XX:+RecordTraining ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(exit-app)
+	ls -l $@
+
+${PC_DYNAMIC_LT_JSA}: ${PC_STATIC_JSA}
+	rm -f ${PC_DYNAMIC_LT_JSA} ${PC_DYNAMIC_LT_JSA}.log
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_STATIC_JSA} -XX:ArchiveClassesAtExit=${PC_DYNAMIC_LT_JSA} \
+	    -Xlog:cds=debug,cds+class=debug,cds+resolve=debug:file=$@.log \
+	    -XX:+RecordTraining ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+	ls -l $@
+
+${PC_ST_CACHED_CODE}: ${PC_DYNAMIC_ST_JSA}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_ST_JSA} -XX:+ReplayTraining -XX:+StoreCachedCode \
+	     -XX:CachedCodeFile=${PC_ST_CACHED_CODE} -XX:CachedCodeMaxSize=512M ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(exit-app)
+	ls -l $@
+
+${PC_LT_CACHED_CODE}: ${PC_DYNAMIC_LT_JSA}
+	${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_LT_JSA} -XX:+ReplayTraining -XX:+StoreCachedCode \
+	     -Xlog:scc,cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=$@.log \
+	     -XX:CachedCodeFile=${PC_LT_CACHED_CODE} -XX:CachedCodeMaxSize=512M ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+	ls -l $@
+
+${PC_CDS_PREIMAGE}: ${PC_APP_UNPACKED}
+	rm -f ${PC_CDS_PREIMAGE} ${PC_CDS}
+	${PREMAIN_JAVA} -XX:+AOTClassLinking \
+               -XX:+ArchiveReflectionData \
+               -XX:+ArchiveDynamicProxies -XX:+ArchiveLoaderLookupCache -XX:CachedCodeMaxSize=512M \
+               -XX:+UnlockDiagnosticVMOptions -XX:+CDSManualFinalImage -XX:CacheDataStore=${PC_CDS} \
+               -Xlog:cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=$@.log \
+               ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+	ls -l $@
+
+${PC_CDS}: ${PC_CDS_PREIMAGE}
+	rm -f ${PC_CDS}
+	${PREMAIN_JAVA} -XX:+AOTClassLinking -XX:CachedCodeMaxSize=512M \
+	       -XX:+UnlockDiagnosticVMOptions -XX:CDSPreimage=${PC_CDS_PREIMAGE} -XX:CacheDataStore=${PC_CDS} \
+	       -Xlog:scc,cds=debug,cds+class=debug,cds+heap=warning,cds+resolve=debug:file=$@.log \
+	       -XX:-AssemblyStepInlining \
+	       ${UNPACKED_CMDLINE} &> $@.out
+	ls -l $@
+	ls -l $@.code
+
+warmup-bl: ${PC_DYNAMIC_BL_JSA} ${JMETER_BIN}
+	time taskset -c "${APP_CPUS}" ${BL_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_BL_JSA} \
+		${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+
+# run with dynamic archive without training data
+warmup-preload: ${PC_DYNAMIC_NOTRAINING_JSA} ${JMETER_BIN}
+	time taskset -c "${APP_CPUS}" ${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_NOTRAINING_JSA} \
+		${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+
+# run with dynamic archive with startup training data
+warmup-st: ${PC_DYNAMIC_ST_JSA} ${JMETER_BIN}
+	time taskset -c "${APP_CPUS}" ${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_ST_JSA} -XX:+ReplayTraining \
+		${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+
+# run with dynamic archive with load training data
+warmup-lt: ${PC_DYNAMIC_LT_JSA} ${JMETER_BIN}
+	time taskset -c "${APP_CPUS}" ${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_LT_JSA} -XX:+ReplayTraining \
+		${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+
+# run with startup training data and aot code
+warmup-st-aot: ${PC_ST_CACHED_CODE} ${JMETER_BIN}
+	time taskset -c "${APP_CPUS}" ${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_ST_JSA} -XX:+ReplayTraining -XX:+LoadCachedCode \
+		-XX:CachedCodeFile=${PC_ST_CACHED_CODE} ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+
+# run with load training data and aot code
+warmup-lt-aot: ${PC_LT_CACHED_CODE} ${JMETER_BIN}
+	time taskset -c "${APP_CPUS}" ${PREMAIN_JAVA} -XX:SharedArchiveFile=${PC_DYNAMIC_LT_JSA} -XX:+ReplayTraining -XX:+LoadCachedCode \
+		-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:-TraceDeoptimization \
+		-Xlog:init -XX:CachedCodeFile=${PC_LT_CACHED_CODE} ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+
+# run with new single-step workflow
+warmup-lt-aot-new: ${PC_CDS}
+	time taskset -c "${APP_CPUS}" ${PREMAIN_JAVA} -XX:CacheDataStore=${PC_CDS} \
+	       -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation \
+	       -Xlog:init ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(run-jmeter)
+	$(exit-app)
+
+run: warmup-bl warmup-preload warmup-st warmup-lt warmup-st-aot warmup-lt-aot warmup-lt-aot-new
+
+run17: ${PC_APP_UNPACKED}
+	time ${JDK17_JAVA} ${UNPACKED_CMDLINE} &> $@.out &
+	$(wait-for-startup)
+	$(exit-app)
+
+
+clean: clean0
+	rm -rf *~ ${PC_REPO}
+
+# clean the the leyden artifacts
+clean0:
+	rm -fv spring-petclinic.* *.out tput-*.log app.pid
diff a/test/hotspot/jtreg/premain/spring-petclinic/bench.sh b/test/hotspot/jtreg/premain/spring-petclinic/bench.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/bench.sh
@@ -0,0 +1,76 @@
+# Copyright (c) 2023, 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.
+
+# Compare the performance of petclinic with two JDK builds
+# - Do the training run 10 times.
+# - After each training run, run petclinic 10 times
+#
+# To compare the results, run result.sh
+
+#------------------------------ configuration --------------------
+OLD_DIR=/jdk3/le4/open/test/hotspot/jtreg/premain/spring-petclinic
+NEW_DIR=/jdk3/le3/open/test/hotspot/jtreg/premain/spring-petclinic
+
+OLD_HOME=/jdk3/bld/le4/images/jdk
+NEW_HOME=/jdk3/bld/le3/images/jdk
+
+OLD_JVM=$OLD_HOME/bin/java
+NEW_JVM=$NEW_HOME/bin/java
+#------------------------------ configuration end-----------------
+
+cd ~/tmp/
+mkdir -p pet-clinic-bench
+cd pet-clinic-bench
+mkdir -p data
+
+CMDLINE="-XX:SharedArchiveFile=spring-petclinic.dynamic.jsa -XX:+ReplayTraining -XX:+LoadCachedCode"
+CMDLINE="$CMDLINE -XX:CachedCodeFile=spring-petclinic.code.jsa -Xlog:init -Xlog:scc=error -Xmx2g"
+CMDLINE="$CMDLINE -cp @petclinic-snapshot/target/unpacked/classpath -DautoQuit=true"
+CMDLINE="$CMDLINE -Dspring.aot.enabled=true org.springframework.samples.petclinic.PetClinicApplication"
+
+(
+set -x
+
+for i in {1..10}; do
+    echo "make old $i"
+    (cd $OLD_DIR; make clean0; make PREMAIN_HOME=$OLD_HOME run 2>&1) > data/make.old.$i
+    grep "Booted and returned" data/make.old.$i
+
+    echo "make new $i"
+    (cd $NEW_DIR; make clean0; make PREMAIN_HOME=$NEW_HOME run 2>&1) > data/make.new.$i
+    grep "Booted and returned" data/make.new.$i
+
+    rm -f data/time.old.$i
+    rm -f data/time.new.$i
+
+    for j in {1..10}; do
+        sync; echo 'after 100' | tclsh
+        (cd $OLD_DIR; $OLD_JVM $CMDLINE 2>&1) > tmp.txt
+        cat tmp.txt >> data/time.old.$i
+        echo $i $j old $(grep "Booted and returned" tmp.txt)
+
+        sync; echo 'after 100' | tclsh
+        (cd $NEW_DIR; $NEW_JVM $CMDLINE 2>&1) > tmp.txt
+        cat tmp.txt >> data/time.new.$i
+        echo $i $j new $(grep "Booted and returned" tmp.txt)
+    done
+done
+) 2>&1 | tee log.txt
diff a/test/hotspot/jtreg/premain/spring-petclinic/petclinic_test_plan.jmx b/test/hotspot/jtreg/premain/spring-petclinic/petclinic_test_plan.jmx
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/petclinic_test_plan.jmx
@@ -0,0 +1,400 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
+  <hashTree>
+    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
+      <stringProp name="TestPlan.comments"></stringProp>
+      <boolProp name="TestPlan.functional_mode">false</boolProp>
+      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
+      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+        <collectionProp name="Arguments.arguments">
+          <elementProp name="PETCLINIC_HOST" elementType="Argument">
+            <stringProp name="Argument.name">PETCLINIC_HOST</stringProp>
+            <stringProp name="Argument.value">localhost</stringProp>
+            <stringProp name="Argument.metadata">=</stringProp>
+          </elementProp>
+          <elementProp name="PETCLINIC_PORT" elementType="Argument">
+            <stringProp name="Argument.name">PETCLINIC_PORT</stringProp>
+            <stringProp name="Argument.value">8080</stringProp>
+            <stringProp name="Argument.metadata">=</stringProp>
+          </elementProp>
+          <elementProp name="CONTEXT_WEB" elementType="Argument">
+            <stringProp name="Argument.name">CONTEXT_WEB</stringProp>
+            <stringProp name="Argument.value"></stringProp>
+            <stringProp name="Argument.metadata">=</stringProp>
+          </elementProp>
+        </collectionProp>
+      </elementProp>
+      <stringProp name="TestPlan.user_define_classpath"></stringProp>
+    </TestPlan>
+    <hashTree>
+      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="User threads" enabled="true">
+        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
+        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Contr�leur Boucle" enabled="true">
+          <boolProp name="LoopController.continue_forever">false</boolProp>
+          <intProp name="LoopController.loops">-1</intProp>
+        </elementProp>
+        <stringProp name="ThreadGroup.num_threads">${__P(THREADS, 40)}</stringProp>
+        <stringProp name="ThreadGroup.ramp_time">0</stringProp>
+        <longProp name="ThreadGroup.start_time">1361531541000</longProp>
+        <longProp name="ThreadGroup.end_time">1361531541000</longProp>
+        <boolProp name="ThreadGroup.scheduler">true</boolProp>
+        <stringProp name="ThreadGroup.duration">${__P(DURATION, 15)}</stringProp>
+        <stringProp name="ThreadGroup.delay"></stringProp>
+        <stringProp name="TestPlan.comments">Original : 500 - 10 - 10</stringProp>
+        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
+      </ThreadGroup>
+      <hashTree>
+        <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Fixed time counter" enabled="false">
+          <stringProp name="ConstantTimer.delay">0</stringProp>
+        </ConstantTimer>
+        <hashTree/>
+        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="Default HTTP parameters" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain">${PETCLINIC_HOST}</stringProp>
+          <stringProp name="HTTPSampler.port">${PETCLINIC_PORT}</stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path"></stringProp>
+          <stringProp name="HTTPSampler.concurrentPool">4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </ConfigTestElement>
+        <hashTree/>
+        <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP cookie manager" enabled="true">
+          <collectionProp name="CookieManager.cookies"/>
+          <boolProp name="CookieManager.clearEachIteration">true</boolProp>
+          <boolProp name="CookieManager.controlledByThreadGroup">false</boolProp>
+        </CookieManager>
+        <hashTree/>
+        <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="User Count" enabled="true">
+          <stringProp name="CounterConfig.start">1</stringProp>
+          <stringProp name="CounterConfig.end">10</stringProp>
+          <stringProp name="CounterConfig.incr">1</stringProp>
+          <stringProp name="CounterConfig.name">count</stringProp>
+          <stringProp name="CounterConfig.format"></stringProp>
+          <boolProp name="CounterConfig.per_user">false</boolProp>
+        </CounterConfig>
+        <hashTree/>
+        <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Pet Count" enabled="true">
+          <stringProp name="CounterConfig.start">1</stringProp>
+          <stringProp name="CounterConfig.end">13</stringProp>
+          <stringProp name="CounterConfig.incr">1</stringProp>
+          <stringProp name="CounterConfig.name">petCount</stringProp>
+          <stringProp name="CounterConfig.format"></stringProp>
+          <boolProp name="CounterConfig.per_user">false</boolProp>
+        </CounterConfig>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Home page" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="CSS" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/resources/css/petclinic.css</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="JS" enabled="false">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/webjars/jquery/jquery.min.js</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Vets" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/vets.html</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Find owner" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/find</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Find owner with lastname=&quot;&quot;" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners?lastName=</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Owner" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Owner" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/edit</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST Edit Owner" enabled="true">
+          <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
+            <collectionProp name="Arguments.arguments">
+              <elementProp name="" elementType="HTTPArgument">
+                <boolProp name="HTTPArgument.always_encode">false</boolProp>
+                <stringProp name="Argument.value">firstName=Test&amp;lastName=${count}&amp;address=1234+Test+St.&amp;city=TestCity&amp;telephone=612345678</stringProp>
+                <stringProp name="Argument.metadata">=</stringProp>
+              </elementProp>
+            </collectionProp>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/edit</stringProp>
+          <stringProp name="HTTPSampler.method">POST</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New visit" enabled="false">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST new visit" enabled="false">
+          <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
+            <collectionProp name="Arguments.arguments">
+              <elementProp name="" elementType="HTTPArgument">
+                <boolProp name="HTTPArgument.always_encode">false</boolProp>
+                <stringProp name="Argument.value">date=2013%2F02%2F22&amp;description=visit</stringProp>
+                <stringProp name="Argument.metadata">=</stringProp>
+              </elementProp>
+            </collectionProp>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
+          <stringProp name="HTTPSampler.method">POST</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Owners" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Variables pr�-d�finies" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
+        </HTTPSamplerProxy>
+        <hashTree/>
+        <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="Results" enabled="true">
+          <boolProp name="ResultCollector.error_logging">false</boolProp>
+          <objProp>
+            <name>saveConfig</name>
+            <value class="SampleSaveConfiguration">
+              <time>true</time>
+              <latency>true</latency>
+              <timestamp>true</timestamp>
+              <success>true</success>
+              <label>true</label>
+              <code>true</code>
+              <message>true</message>
+              <threadName>true</threadName>
+              <dataType>true</dataType>
+              <encoding>false</encoding>
+              <assertions>true</assertions>
+              <subresults>true</subresults>
+              <responseData>false</responseData>
+              <samplerData>false</samplerData>
+              <xml>false</xml>
+              <fieldNames>false</fieldNames>
+              <responseHeaders>false</responseHeaders>
+              <requestHeaders>false</requestHeaders>
+              <responseDataOnError>false</responseDataOnError>
+              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+              <assertionsResultsToSave>0</assertionsResultsToSave>
+              <bytes>true</bytes>
+              <threadCounts>true</threadCounts>
+            </value>
+          </objProp>
+          <stringProp name="filename"></stringProp>
+        </ResultCollector>
+        <hashTree/>
+        <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregated report" enabled="true">
+          <boolProp name="ResultCollector.error_logging">false</boolProp>
+          <objProp>
+            <name>saveConfig</name>
+            <value class="SampleSaveConfiguration">
+              <time>true</time>
+              <latency>true</latency>
+              <timestamp>true</timestamp>
+              <success>true</success>
+              <label>true</label>
+              <code>true</code>
+              <message>true</message>
+              <threadName>true</threadName>
+              <dataType>true</dataType>
+              <encoding>false</encoding>
+              <assertions>true</assertions>
+              <subresults>true</subresults>
+              <responseData>false</responseData>
+              <samplerData>false</samplerData>
+              <xml>false</xml>
+              <fieldNames>false</fieldNames>
+              <responseHeaders>false</responseHeaders>
+              <requestHeaders>false</requestHeaders>
+              <responseDataOnError>false</responseDataOnError>
+              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+              <assertionsResultsToSave>0</assertionsResultsToSave>
+              <bytes>true</bytes>
+              <threadCounts>true</threadCounts>
+            </value>
+          </objProp>
+          <stringProp name="filename"></stringProp>
+        </ResultCollector>
+        <hashTree/>
+      </hashTree>
+    </hashTree>
+  </hashTree>
+</jmeterTestPlan>
diff a/test/hotspot/jtreg/premain/spring-petclinic/premain-patch-noautoquit.diff b/test/hotspot/jtreg/premain/spring-petclinic/premain-patch-noautoquit.diff
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/premain-patch-noautoquit.diff
@@ -0,0 +1,85 @@
+diff --git a/pom.xml b/pom.xml
+index 8a3d6f1..2d0f914 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -209,10 +209,12 @@
+           </execution>
+         </executions>
+       </plugin>
++<!--
+       <plugin>
+         <groupId>org.graalvm.buildtools</groupId>
+         <artifactId>native-maven-plugin</artifactId>
+       </plugin>
++-->
+       <plugin>
+         <groupId>org.springframework.boot</groupId>
+         <artifactId>spring-boot-maven-plugin</artifactId>
+@@ -230,8 +232,20 @@
+                 <java.source>${java.version}</java.source>
+                 <java.target>${java.version}</java.target>
+               </additionalProperties>
++              <image>
++                <builder>paketobuildpacks/builder:tiny</builder>
++                <env>
++                  <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
++                </env>
++              </image>
+             </configuration>
+           </execution>
++          <execution>
++            <id>process-aot</id>
++            <goals>
++              <goal>process-aot</goal>
++            </goals>
++          </execution>
+         </executions>
+       </plugin>
+       <plugin>
+@@ -434,4 +448,4 @@
+     </profile>
+   </profiles>
+ 
+-</project>
+\ No newline at end of file
++</project>
+diff --git a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
+index ac6e150..491ec02 100644
+--- a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
++++ b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
+@@ -18,6 +18,8 @@ package org.springframework.samples.petclinic;
+ 
+ import org.springframework.boot.SpringApplication;
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
++import org.springframework.boot.context.ApplicationPidFileWriter;
++import org.springframework.boot.context.event.ApplicationStartedEvent;
+ import org.springframework.context.annotation.ImportRuntimeHints;
+ 
+ /**
+@@ -31,7 +33,13 @@ import org.springframework.context.annotation.ImportRuntimeHints;
+ public class PetClinicApplication {
+ 
+ 	public static void main(String[] args) {
+-		SpringApplication.run(PetClinicApplication.class, args);
++		SpringApplication application = new SpringApplication(PetClinicApplication.class);
++		ApplicationPidFileWriter pidWriter = new ApplicationPidFileWriter();
++		application.addListeners(pidWriter);
++		long begin = System.currentTimeMillis();
++		application.run(args);
++		long end = System.currentTimeMillis();
++		System.out.println("#### Booted and returned in " + (end - begin) + "ms");
+ 	}
+ 
+ }
+diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
+index 5d3eeed..404f06e 100644
+--- a/src/main/resources/application.properties
++++ b/src/main/resources/application.properties
+@@ -23,3 +23,7 @@ logging.level.org.springframework=INFO
+ 
+ # Maximum time static resources should be cached
+ spring.web.resources.cache.cachecontrol.max-age=12h
++
++management.endpoints.web.exposure.include=*
++management.endpoint.shutdown.enabled=true
++endpoints.shutdown.enabled=true
diff a/test/hotspot/jtreg/premain/spring-petclinic/premain-patch.diff b/test/hotspot/jtreg/premain/spring-petclinic/premain-patch.diff
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/premain-patch.diff
@@ -0,0 +1,79 @@
+diff --git a/pom.xml b/pom.xml
+index 8a3d6f1..6d148ff 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -209,10 +209,12 @@
+           </execution>
+         </executions>
+       </plugin>
++<!--
+       <plugin>
+         <groupId>org.graalvm.buildtools</groupId>
+         <artifactId>native-maven-plugin</artifactId>
+       </plugin>
++-->
+       <plugin>
+         <groupId>org.springframework.boot</groupId>
+         <artifactId>spring-boot-maven-plugin</artifactId>
+@@ -230,8 +232,20 @@
+                 <java.source>${java.version}</java.source>
+                 <java.target>${java.version}</java.target>
+               </additionalProperties>
++              <image>
++                <builder>paketobuildpacks/builder:tiny</builder>
++                <env>
++                  <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
++                </env>
++              </image>
+             </configuration>
+           </execution>
++          <execution>
++            <id>process-aot</id>
++            <goals>
++              <goal>process-aot</goal>
++            </goals>
++          </execution>
+         </executions>
+       </plugin>
+       <plugin>
+diff --git a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
+index ac6e150..fe5c747 100644
+--- a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
++++ b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
+@@ -16,6 +16,9 @@
+ 
+ package org.springframework.samples.petclinic;
+ 
++import java.lang.management.ManagementFactory;
++import java.lang.management.RuntimeMXBean;
++
+ import org.springframework.boot.SpringApplication;
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+ import org.springframework.context.annotation.ImportRuntimeHints;
+@@ -31,7 +34,26 @@ import org.springframework.context.annotation.ImportRuntimeHints;
+ public class PetClinicApplication {
+ 
+ 	public static void main(String[] args) {
++		long mainStart = System.currentTimeMillis();
++		RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
++		// This includes all the time spent inside the JVM before main() is reached
++		// (since os::Posix::init is called and initial_time_count is initialized).
++		long vmStart = runtimeMXBean.getStartTime();
++		long maxBeanOverHead = System.currentTimeMillis() - mainStart;
++
+ 		SpringApplication.run(PetClinicApplication.class, args);
++		long end = System.currentTimeMillis();
++		Boolean quit = Boolean.getBoolean("autoQuit");
++		if (quit) {
++			System.out.println("#### Booted and returned in " + (end - vmStart - maxBeanOverHead) + "ms");
++			System.out.println("#### (debug) mainStart = " + mainStart);
++			System.out.println("#### (debug) vmStart = " + vmStart);
++			System.out.println("#### (debug) before main (mainStart - vmStart) = " + (mainStart - vmStart));
++			System.out.println("#### (debug) maxBeanOverHead = " + maxBeanOverHead);
++			System.out.println("#### (debug) end = " + end);
++			System.exit(0);
++		}
++
+ 	}
+ 
+ }
diff a/test/hotspot/jtreg/premain/spring-petclinic/result.sh b/test/hotspot/jtreg/premain/spring-petclinic/result.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/result.sh
@@ -0,0 +1,58 @@
+# Copyright (c) 2023, 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.
+
+cd ~/tmp/
+mkdir -p pet-clinic-bench
+cd pet-clinic-bench
+
+function geomean () {
+    (for i in $*; do echo $i; done) | \
+        awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++} END{m=tot/c; printf "%.2f\n", E^m}' -
+}
+
+function stdev () {
+    (for i in $*; do echo $i; done) | \
+        awk '{for(i=1;i<=NF;i++) {sum[i] += $i; sumsq[i] += ($i)^2}}
+          END {for (i=1;i<=NF;i++) {
+          printf "%.2f\n", sqrt((sumsq[i]-sum[i]^2/NR)/NR)}
+         }' -
+}
+
+function count () {
+    echo $* | wc -w
+}
+
+# This tests overall improvements
+echo "Final run: {static + dynamic} archive, +ReplayTraining and +LoadCachedCode"
+for i in old new; do
+    nums=$(grep Booted data/time.$i.* | sed -e 's/.*in //g' -e 's/ms//g')
+    nums="$nums $(for j in data/make.$i.*; do grep Booted $j | tail -1 | sed -e 's/.*in //g' -e 's/ms//g'; done)"
+    printf "%s %8.2f ms %8.2f ms (%d samples)\n" $i $(geomean $nums) $(stdev $nums) $(count $nums)
+done
+
+# This tests improvements in CDS only.
+echo ""
+echo "Static archive only"
+for i in old new; do
+    nums=$(for j in data/make.$i.*; do grep Booted $j | head -2 | tail -1 | sed -e 's/.*in //g' -e 's/ms//g'; done)
+    printf "%s %8.2f ms %8.2f ms (%d samples)\n" $i $(geomean $nums) $(stdev $nums) $(count $nums)
+done
+
diff a/test/hotspot/jtreg/premain/spring-petclinic/warmup_result.sh b/test/hotspot/jtreg/premain/spring-petclinic/warmup_result.sh
--- /dev/null
+++ b/test/hotspot/jtreg/premain/spring-petclinic/warmup_result.sh
@@ -0,0 +1,22 @@
+get_tput_results() {
+  local tput_file=$1
+  if [ ! -f ${tput_file} ]; then
+    error "jmeter throughput file ${tput_file} not found."
+    exit 1
+  fi
+  workdir=`mktemp -d`
+  awk '/summary \+/' ${tput_file} > ${workdir}/tputlines
+  awk '{ print $5 }' ${workdir}/tputlines > ${workdir}/time
+  awk -F ":" 'BEGIN { total=0 } { total += $3; print total }' ${workdir}/time > ${workdir}/time.tmp
+  awk '{ print $7 }' ${workdir}/tputlines | cut -d '/' -f 1 > ${workdir}/rampup
+
+  echo "time,tput" > ${tput_file}.rampup.csv
+  paste -d "," ${workdir}/time.tmp ${workdir}/rampup >> ${tput_file}.rampup.csv
+  rm -fr ${workdir}
+}
+
+for file in `ls tput-*.log`; do
+  get_tput_results ${file}
+done
+
+
diff a/test/hotspot/jtreg/runtime/cds/CDSMapReader.java b/test/hotspot/jtreg/runtime/cds/CDSMapReader.java
--- a/test/hotspot/jtreg/runtime/cds/CDSMapReader.java
+++ b/test/hotspot/jtreg/runtime/cds/CDSMapReader.java
@@ -295,11 +295,11 @@
             lineCount = 0;
         }
     }
 
     private static void mustContain(HashMap<Long, HeapObject> allObjects, Field field, long pointer, boolean isNarrow) {
-        if (allObjects.get(pointer) == null) {
+        if (pointer != 0x0 && allObjects.get(pointer) == null) {
             throw new RuntimeException((isNarrow ? "narrowOop" : "oop") + " pointer 0x" + Long.toHexString(pointer) +
                                        " on line " + field.lineCount + " doesn't point to a valid heap object");
         }
     }
 
diff a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2023, 2024, 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 In Leyden repo, classes that are linked during the assembly phase can
+ *       skip verification constraint checks. See JDK-8317269.
+ *       NOTE: this feature is not included when JDK-8315737 is upstreamed to the mainline.
+ * @requires vm.cds
+ * @requires vm.cds.supports.aot.class.linking
+ * @summary Test for verification of classes that are aot-linked
+ * @library /test/jdk/lib/testlibrary
+ *          /test/lib
+ *          /test/hotspot/jtreg/runtime/cds/appcds
+ *          /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build GoodOldClass BadOldClass BadOldClass2 BadNewClass BadNewClass2
+ * @build AOTClassLinkingVerification
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app1.jar
+ *                 AOTClassLinkingVerificationApp
+ *                 Unlinked UnlinkedSuper
+ *                 BadOldClass
+ *                 BadOldClass2
+ *                 BadNewClass
+ *                 BadNewClass2
+ *                 GoodOldClass Vehicle Car
+ *                 Util
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app2.jar
+ *                 UnlinkedSub
+ *                 Foo NotFoo
+ * @run driver AOTClassLinkingVerification
+ */
+
+import java.io.File;
+import java.lang.invoke.MethodHandles;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.whitebox.WhiteBox;
+
+public class AOTClassLinkingVerification {
+    static final String app1Jar = ClassFileInstaller.getJarPath("app1.jar");
+    static final String app2Jar = ClassFileInstaller.getJarPath("app2.jar");
+    static final String wbJar = TestCommon.getTestJar("WhiteBox.jar");
+    static final String bootAppendWhiteBox = "-Xbootclasspath/a:" + wbJar;
+    static final String mainClass = AOTClassLinkingVerificationApp.class.getName();
+
+    public static void main(String[] args) throws Exception {
+        // Dump without app2.jar so:
+        //  - Unlinked can be resolved, but UnlinkedSuper UnlinkedSub cannot be resolved,
+        //    so Unlinked cannot be verified at dump time.
+        //  - BadOldClass2 can be resolved, but Foo and NotFoo cannot be resolved,
+        //    so BadOldClass2 cannot be verified at dump time.
+        //  - BadNewClass2 can be resolved, but Foo and NotFoo cannot be resolved,
+        //    so BadNewClass2 cannot be verified at dump time.
+        TestCommon.testDump(app1Jar, TestCommon.list("Unlinked",
+                                                     "BadOldClass",
+                                                     "BadOldClass2",
+                                                     "BadNewClass",
+                                                     "BadNewClass2",
+                                                     "GoodOldClass"),
+                            bootAppendWhiteBox,
+                            "-XX:+AOTClassLinking",
+                            "-Xlog:cds+class=debug");
+
+        TestCommon.run("-cp", app1Jar + File.pathSeparator + app2Jar,
+                       "-XX:+UnlockDiagnosticVMOptions",
+                       "-XX:+WhiteBoxAPI",
+                       bootAppendWhiteBox,
+                       "AOTClassLinkingVerificationApp", app1Jar)
+            .assertNormalExit();
+    }
+}
+
+class AOTClassLinkingVerificationApp {
+    static WhiteBox wb = WhiteBox.getWhiteBox();
+    static ClassLoader classLoader = AOTClassLinkingVerificationApp.class.getClassLoader();
+    static File app1Jar;
+
+    public static void main(String[] args) throws Exception {
+        app1Jar = new File(args[0]);
+        assertNotShared(UnlinkedSub.class);
+        assertShared(UnlinkedSuper.class);
+        assertShared(Unlinked.class);
+        assertNotShared(Foo.class);
+        assertNotShared(NotFoo.class);
+        String s = Unlinked.doit();
+        if (!s.equals("heyhey")) {
+            throw new RuntimeException("Unlinked.doit() returns wrong result: " + s);
+        }
+
+        Class cls_BadOldClass = Class.forName("BadOldClass", false, classLoader);
+        assertShared(cls_BadOldClass);
+        try {
+            cls_BadOldClass.newInstance();
+            throw new RuntimeException("BadOldClass cannot be verified");
+        } catch (VerifyError expected) {}
+
+        Class cls_BadOldClass2 = Class.forName("BadOldClass2", false, classLoader);
+        assertShared(cls_BadOldClass2);
+        try {
+            cls_BadOldClass2.newInstance();
+            throw new RuntimeException("BadOldClass2 cannot be verified");
+        } catch (VerifyError expected) {}
+
+        Class cls_BadNewClass = Class.forName("BadNewClass", false, classLoader);
+        assertShared(cls_BadNewClass);
+        try {
+            cls_BadNewClass.newInstance();
+            throw new RuntimeException("BadNewClass cannot be verified");
+        } catch (VerifyError expected) {}
+
+        Class cls_BadNewClass2 = Class.forName("BadNewClass2", false, classLoader);
+        assertShared(cls_BadNewClass2);
+        try {
+            cls_BadNewClass2.newInstance();
+            throw new RuntimeException("BadNewClass2 cannot be verified");
+        } catch (VerifyError expected) {}
+
+
+        // Although Vehicle and Car are not specified in the classlist, they are archived as
+        // they were used during dumptime verification of GoodOldClass
+        assertAlreadyLoaded("Vehicle");
+        assertAlreadyLoaded("Car");
+        assertAlreadyLoaded("GoodOldClass");
+
+        assertShared(GoodOldClass.class);
+        assertShared(Vehicle.class);
+        assertShared(Car.class);
+
+        GoodOldClass.doit(); // Should not fail
+    }
+
+    static void assertShared(Class c) {
+        if (!wb.isSharedClass(c)) {
+            throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be true");
+        }
+    }
+
+    static void assertNotShared(Class c) {
+        if (wb.isSharedClass(c)) {
+            throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be false");
+        }
+    }
+
+    static void assertAlreadyLoaded(String className) throws Exception {
+        byte[] data = Util.getClassFileFromJar(app1Jar, className);
+        try {
+            MethodHandles.lookup().defineClass(data);
+        } catch (LinkageError e) {
+            if (e.getMessage().contains("duplicate class definition for " + className)) {
+                return;
+            } else {
+                throw e;
+            }
+        }
+        throw new RuntimeException(className + " must have already been loaded");
+    }
+}
+
+
+class Unlinked {
+    static String doit() {
+        UnlinkedSuper sup = new UnlinkedSub();
+        return sup.doit();
+    }
+}
+
+abstract class UnlinkedSuper {
+    abstract String doit();
+}
+
+class UnlinkedSub extends UnlinkedSuper {
+    String doit() {
+        return "heyhey";
+    }
+}
+
+class Foo {}
+class NotFoo {}
+
+class Vehicle {}
+class Car extends Vehicle {}
diff a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+// When -XX:+PreloadSharedClasses is enabled, this class will be
+// archived in the unlinked state. When we try to use this class
+// at runtime, a VerifyError should be thrown.
+
+super public class BadNewClass
+	version 49:0
+{
+
+
+public Method "<init>":"()V"
+	stack 1 locals 1
+{
+		aload_0;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		return;
+}
+
+    /*
+     * The following method tries to return an Object as a String.
+     * Verifier should fail.
+     */
+public Method doit:"()Ljava/lang/String;"
+	stack 2 locals 1
+{
+                new	class java/lang/Object;
+		dup;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		astore_0;
+		aload_0;
+		areturn;   // tries to return an Object as a String
+}
+
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+// When -XX:+PreloadSharedClasses is enabled, this class will be
+// archived in the unlinked state. When we try to use this class
+// at runtime, a VerifyError should be thrown.
+
+super public class BadNewClass2
+	version 52:0
+{
+
+
+public Method "<init>":"()V"
+	stack 1 locals 1
+{
+		aload_0;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		return;
+}
+
+    /*
+     * The following method tries to return a NotFoo as a Foo.
+     * Verifier should fail.
+     */
+public Method doit:"()LFoo;"
+	stack 2 locals 1
+{
+                new	class NotFoo;
+		dup;
+		invokespecial	Method NotFoo."<init>":"()V";
+		astore_0;
+		aload_0;
+		areturn;   // tries to return a NotFoo as a Foo
+}
+
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+// When -XX:+PreloadSharedClasses is enabled, this class will be
+// archived in the unlinked state. When we try to use this class
+// at runtime, a VerifyError should be thrown.
+
+super public class BadOldClass
+	version 49:0
+{
+
+
+public Method "<init>":"()V"
+	stack 1 locals 1
+{
+		aload_0;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		return;
+}
+
+    /*
+     * The following method tries to return an Object as a String.
+     * Verifier should fail.
+     */
+public Method doit:"()Ljava/lang/String;"
+	stack 2 locals 1
+{
+                new	class java/lang/Object;
+		dup;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		astore_0;
+		aload_0;
+		areturn;   // tries to return an Object as a String
+}
+
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+// When -XX:+PreloadSharedClasses is enabled, this class will be
+// archived in the unlinked state. When we try to use this class
+// at runtime, a VerifyError should be thrown.
+
+super public class BadOldClass2
+	version 49:0
+{
+
+
+public Method "<init>":"()V"
+	stack 1 locals 1
+{
+		aload_0;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		return;
+}
+
+    /*
+     * The following method tries to return a NotFoo as a Foo.
+     * Verifier should fail.
+     */
+public Method doit:"()LFoo;"
+	stack 2 locals 1
+{
+                new	class NotFoo;
+		dup;
+		invokespecial	Method NotFoo."<init>":"()V";
+		astore_0;
+		aload_0;
+		areturn;   // tries to return a NotFoo as a Foo
+}
+
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+// When -XX:+PreloadSharedClasses is enabled, this class will be
+// archived in the unlinked state. When we try to use this class
+// at runtime, a VerifyError should be thrown.
+
+super public class GoodOldClass
+	version 49:0
+{
+
+
+public Method "<init>":"()V"
+	stack 1 locals 1
+{
+		aload_0;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		return;
+}
+
+
+public static Method doit:"()LVehicle;"
+	stack 2 locals 1
+{
+                new	class Car;
+		dup;
+		invokespecial	Method Car."<init>":"()V";
+		astore_0;
+		aload_0;
+		areturn;   // tries to return a Car as a Vehicle
+}
+
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/applications/HelidonQuickStartSE.java b/test/hotspot/jtreg/runtime/cds/appcds/applications/HelidonQuickStartSE.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/applications/HelidonQuickStartSE.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Map;
+import jdk.test.lib.StringArrayUtils;
+import jdk.test.lib.artifacts.Artifact;
+import jdk.test.lib.artifacts.ArtifactResolver;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+
+/*
+ * @test id=static
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run HelidonQuickStartSE with the classic static archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 HelidonQuickStartSE STATIC
+ */
+
+/*
+ * @test id=dynamic
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run HelidonQuickStartSE with the classic dynamic archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 HelidonQuickStartSE DYNAMIC
+ */
+
+/*
+ * @test id=leyden
+ * @key external-dep
+ * @requires vm.cds
+ * @requires vm.cds.write.archived.java.heap
+ * @summary un HelidonQuickStartSE with the Leyden workflow
+ * @library /test/lib
+ * @run driver/timeout=120 HelidonQuickStartSE LEYDEN
+ */
+
+// Test CDS with the example program in https://helidon.io/docs/v4/se/guides/quickstart
+//
+// NOTE: if you have not set up an artifactory, you can create helidon-quickstart-se-4.0.7.zip by:
+//
+// - Make a clone of https://github.com/openjdk/leyden/tree/premain
+// - Change to the directory test/hotspot/jtreg/premain/helidon-quickstart-se
+// - Edit the Makefile and ../lib/DemoSupport.gmk as necessary
+// - Run the command "make artifact"
+//
+// Then, you can add the following to your jtreg command-line to run the test cases in this directory:
+// -vmoption:-Djdk.test.lib.artifacts.helidon-quickstart-se=/my/repo/test/hotspot/jtreg/premain/helidon-quickstart-se/helidon-quickstart-se
+
+@Artifact(organization = "io.helidon", name = "helidon-quickstart-se", revision = "4.0.7", extension = "zip")
+public class HelidonQuickStartSE {
+    public static void main(String args[]) throws Exception {
+        String cp = getArtifact();
+        HelidonQuickStartSETester tester = new HelidonQuickStartSETester(cp);
+        tester.run(args);
+        System.out.println(cp);
+    }
+
+    private static String getArtifact() throws Exception {
+        String sep = File.separator;
+        Map<String, Path> artifacts = ArtifactResolver.resolve(HelidonQuickStartSE.class);
+        Path path = artifacts.get("io.helidon.helidon-quickstart-se-4.0.7");
+        return path.toString() + sep + "target" + sep + "helidon-quickstart-se.jar";
+    }
+
+    static class HelidonQuickStartSETester extends CDSAppTester {
+        String cp;
+
+        HelidonQuickStartSETester(String cp) {
+            super("HelidonQuickStartSE");
+            this.cp = cp;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-DautoQuit=true",
+                "-Dserver.port=0", // use system-assigned port
+
+                // This program may run very slowly in debug builds if VerifyDependencies is enabled.
+                "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-VerifyDependencies",
+            };
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return cp;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            String cmdLine[] = new String[] {
+                "io.helidon.examples.quickstart.se.Main", 
+            };
+
+            if (runMode.isProductionRun()) {
+                cmdLine = StringArrayUtils.concat("-Xlog:scc=error", cmdLine);
+            }
+            return cmdLine;
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (!runMode.isStaticDump()) {
+                out.shouldContain("Booted and returned in ");
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/applications/JavacBench.java b/test/hotspot/jtreg/runtime/cds/appcds/applications/JavacBench.java
--- a/test/hotspot/jtreg/runtime/cds/appcds/applications/JavacBench.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/applications/JavacBench.java
@@ -36,12 +36,22 @@
  * @requires vm.cds
  * @library /test/lib
  * @run driver JavacBench DYNAMIC
  */
 
+/*
+ * @test id=leyden
+ * @summary Run JavacBenchApp with Leyden workflow
+ * @requires vm.cds
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/lib
+ * @run driver JavacBench LEYDEN
+ */
+
 import jdk.test.lib.cds.CDSAppTester;
 import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
 
 public class JavacBench {
     static String mainClass = JavacBenchApp.class.getName();
     static String appJar;
 
diff a/test/hotspot/jtreg/runtime/cds/appcds/applications/MicronautFirstApp.java b/test/hotspot/jtreg/runtime/cds/appcds/applications/MicronautFirstApp.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/applications/MicronautFirstApp.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Map;
+import jdk.test.lib.StringArrayUtils;
+import jdk.test.lib.artifacts.Artifact;
+import jdk.test.lib.artifacts.ArtifactResolver;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+
+/*
+ * @test id=static
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run MicronautFirstApp with the classic static archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 MicronautFirstApp STATIC
+ */
+
+/*
+ * @test id=dynamic
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run MicronautFirstApp with the classic dynamic archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 MicronautFirstApp DYNAMIC
+ */
+
+/*
+ * @test id=leyden
+ * @key external-dep
+ * @requires vm.cds
+ * @requires vm.cds.write.archived.java.heap
+ * @summary un MicronautFirstApp with the Leyden workflow
+ * @library /test/lib
+ * @run driver/timeout=120 MicronautFirstApp LEYDEN
+ */
+
+// Test CDS with the example program in https://guides.micronaut.io/latest/creating-your-first-micronaut-app-maven-java.html
+//
+// NOTE: if you have not set up an artifactory, you can create micronaut-first-app-1.0.0.zip
+//
+// - Make a clone of https://github.com/openjdk/leyden/tree/premain
+// - Change to the directory test/hotspot/jtreg/premain/micronaut-first-app
+// - Edit the Makefile and ../lib/DemoSupport.gmk as necessary
+// - Run the command "make artifact"
+//
+// Then, you can add the following to your jtreg command-line to run the test cases in this directory:
+// -vmoption:-Djdk.test.lib.artifacts.micronaut-first-app=/my/repo/test/hotspot/jtreg/premain/micronaut-first-app/download/target/
+
+@Artifact(organization = "io.micronaut", name = "micronaut-first-app", revision = "1.0.0", extension = "zip")
+public class MicronautFirstApp {
+    public static void main(String args[]) throws Exception {
+        String cp = getArtifact();
+        MicronautFirstAppTester tester = new MicronautFirstAppTester(cp);
+        tester.run(args);
+        System.out.println(cp);
+    }
+
+    private static String getArtifact() throws Exception {
+        Map<String, Path> artifacts = ArtifactResolver.resolve(MicronautFirstApp.class);
+        Path path = artifacts.get("io.micronaut.micronaut-first-app-1.0.0");
+        return path.toString() + File.separator + "default-0.1.jar";
+    }
+
+    static class MicronautFirstAppTester extends CDSAppTester {
+        String cp;
+
+        MicronautFirstAppTester(String cp) {
+            super("MicronautFirstApp");
+            this.cp = cp;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-DautoQuit=true",
+                "-Dmicronaut.server.port=0", // use system-assigned port
+
+                // This program may run very slowly in debug builds if VerifyDependencies is enabled.
+                "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-VerifyDependencies",
+            };
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return cp;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            String cmdLine[] = new String[] {
+                "example.micronaut.Application", 
+            };
+
+            if (runMode.isProductionRun()) {
+                cmdLine = StringArrayUtils.concat("-Xlog:scc=error", cmdLine);
+            }
+            return cmdLine;
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (!runMode.isStaticDump()) {
+                out.shouldContain("Booted and returned in ");
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/applications/QuarkusGettingStarted.java b/test/hotspot/jtreg/runtime/cds/appcds/applications/QuarkusGettingStarted.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/applications/QuarkusGettingStarted.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Map;
+import jdk.test.lib.StringArrayUtils;
+import jdk.test.lib.artifacts.Artifact;
+import jdk.test.lib.artifacts.ArtifactResolver;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+
+/*
+ * @test id=static
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run QuarkusGettingStarted with the classic static archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 QuarkusGettingStarted STATIC
+ */
+
+/*
+ * @test id=dynamic
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run QuarkusGettingStarted with the classic dynamic archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 QuarkusGettingStarted DYNAMIC
+ */
+
+/*
+ * @test id=leyden
+ * @key external-dep
+ * @requires vm.cds
+ * @requires vm.cds.write.archived.java.heap
+ * @summary un QuarkusGettingStarted with the Leyden workflow
+ * @library /test/lib
+ * @run driver/timeout=120 QuarkusGettingStarted LEYDEN
+ */
+
+// Test CDS with the example program in https://quarkus.io/guides/getting-started
+//
+// NOTE: if you have not set up an artifactory, you can create quarkus-getting-started-1.0.0b.zip by:
+//
+// - Make a clone of https://github.com/openjdk/leyden/tree/premain
+// - Change to the directory test/hotspot/jtreg/premain/quarkus-getting-started
+// - Edit the Makefile and ../lib/DemoSupport.gmk as necessary
+// - Run the command "make artifact"
+//
+// Then, you can add the following to your jtreg command-line to run the test cases in this directory:
+// -vmoption:-Djdk.test.lib.artifacts.quarkus-getting-started=/my/repo/test/hotspot/jtreg/premain/quarkus-getting-started/getting-started/target
+
+@Artifact(organization = "io.quarkus", name = "quarkus-getting-started", revision = "1.0.0b", extension = "zip")
+public class QuarkusGettingStarted {
+    public static void main(String args[]) throws Exception {
+        String cp = getArtifact();
+        QuarkusGettingStartedTester tester = new QuarkusGettingStartedTester(cp);
+        tester.run(args);
+        System.out.println(cp);
+    }
+
+    private static String getArtifact() throws Exception {
+        Map<String, Path> artifacts = ArtifactResolver.resolve(QuarkusGettingStarted.class);
+        Path path = artifacts.get("io.quarkus.quarkus-getting-started-1.0.0b");
+        return path.toString() + File.separator + "getting-started-1.0.0-SNAPSHOT-runner.jar";
+    }
+
+    static class QuarkusGettingStartedTester extends CDSAppTester {
+        String cp;
+
+        QuarkusGettingStartedTester(String cp) {
+            super("QuarkusGettingStarted");
+            this.cp = cp;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-DautoQuit=true",
+                "-Dquarkus.http.port=0", // use system-assigned port
+
+                // This program may run very slowly in debug builds if VerifyDependencies is enabled.
+                "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-VerifyDependencies",
+            };
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return cp;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            String cmdLine[] = new String[] {
+                "Main", 
+            };
+
+            if (runMode.isProductionRun()) {
+                cmdLine = StringArrayUtils.concat("-Xlog:scc=error", cmdLine);
+            }
+            return cmdLine;
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (!runMode.isStaticDump()) {
+                out.shouldContain("Booted and returned in ");
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/applications/README.md b/test/hotspot/jtreg/runtime/cds/appcds/applications/README.md
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/applications/README.md
@@ -0,0 +1,1 @@
+This directory runs some non-trivial applications with CDS enabled.
diff a/test/hotspot/jtreg/runtime/cds/appcds/applications/SpringPetClinic.java b/test/hotspot/jtreg/runtime/cds/appcds/applications/SpringPetClinic.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/applications/SpringPetClinic.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2023, 2024, 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 id=static
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run Spring Pet Clinic demo with the classic static archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 SpringPetClinic STATIC
+ */
+
+/*
+ * @test id=dynamic
+ * @key external-dep
+ * @requires vm.cds
+ * @summary run Spring Pet Clinic demo with the classic dynamic archive workflow
+ * @library /test/lib
+ * @run driver/timeout=120 SpringPetClinic DYNAMIC
+ */
+
+/*
+ * @test id=leyden
+ * @key external-dep
+ * @requires vm.cds
+ * @requires vm.cds.write.archived.java.heap
+ * @summary run Spring Pet Clinic demo with leyden-premain "new workflow"
+ * @library /test/lib
+ * @run driver/timeout=120 SpringPetClinic LEYDEN
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.nio.file.Path;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import jdk.test.lib.StringArrayUtils;
+import jdk.test.lib.artifacts.Artifact;
+import jdk.test.lib.artifacts.ArtifactResolver;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+
+// NOTE: if you have not set up an artifactory, you can create spring-petclinic-3.2.0.zip by:
+//
+// - Make a clone of https://github.com/openjdk/leyden/tree/premain
+// - Change to the directory test/hotspot/jtreg/premain/spring-petclinic
+// - Edit the Makefile
+// - Run the command "make unpack"
+//
+// Then, you can add the following to your jtreg command-line to run the test cases in this directory:
+// -vmoption:-Djdk.test.lib.artifacts.spring-petclinic=/repo/test/hotspot/jtreg/premain/spring-petclinic/petclinic-snapshot/target/spring-petclinic-3.2.0.zip
+
+@Artifact(organization = "org.springframework.samples", name = "spring-petclinic", revision = "3.2.0", extension = "zip", unpack = false)
+public class SpringPetClinic {
+    public static void main(String args[]) throws Exception {
+        String cp = getArtifact();
+        SpringPetClinicTester tester = new SpringPetClinicTester(cp);
+        tester.run(args);
+    }
+
+    private static String getArtifact() throws Exception {
+        File cpFile = new File("petclinic-snapshot/target/unpacked/classpath");
+        if (!cpFile.exists()) {
+            long started = System.currentTimeMillis();
+            System.out.println("Download and extract spring-petclinic");
+
+            Map<String, Path> artifacts = ArtifactResolver.resolve(SpringPetClinic.class);
+            System.out.println(artifacts);
+            Path zip = artifacts.get("org.springframework.samples.spring-petclinic-3.2.0");
+
+            long elapsed = System.currentTimeMillis() - started;
+            System.out.println("Resolved artifacts in " + elapsed + " ms");
+
+            extractZip(zip.toString(), new File("."));
+        }
+
+        return "@" + cpFile.toString();
+    }
+
+    static void extractZip(String srcFile, File outDir) throws Exception {
+        long zipSize = (new File(srcFile)).length();
+        System.out.println("Extracting from " + srcFile + " (" + zipSize + " bytes) to " + outDir);
+
+        long started = System.currentTimeMillis();
+        try (ZipFile zipFile = new ZipFile(srcFile)) {
+            int numFiles = 0;
+            long numBytes = 0;
+            Enumeration<? extends ZipEntry> e = zipFile.entries();
+
+            byte buffer[] = new byte[1024];
+            while (e.hasMoreElements()) {
+                ZipEntry entry = e.nextElement();
+                if (!entry.isDirectory()) {
+                    File destinationPath = new File(outDir, entry.getName());
+                    destinationPath.getParentFile().mkdirs();
+
+                    numFiles ++;
+                    try (
+                         BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
+                         FileOutputStream fos = new FileOutputStream(destinationPath);
+                         BufferedOutputStream bos = new BufferedOutputStream(fos, 1024)) {
+                        int n;
+                        while ((n = bis.read(buffer, 0, 1024)) != -1) {
+                            bos.write(buffer, 0, n);
+                            numBytes += n;
+                        }
+                    }
+                }
+            }
+            long elapsed = System.currentTimeMillis() - started;
+            System.out.println("Extracted " + numFiles + " file(s) = " + numBytes + " bytes in " + elapsed + " ms");
+        }
+    }
+
+
+    static class SpringPetClinicTester extends CDSAppTester {
+        String cp;
+
+        SpringPetClinicTester(String cp) {
+            super("SpringPetClinic");
+            this.cp = cp;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-Xlog:init",
+                "-DautoQuit=true",
+                "-Dspring.output.ansi.enabled=NEVER",
+                "-Dspring.aot.enabled=true",
+                "-Dserver.port=0", // use system-assigned port
+
+                // PetClinic runs very slowly in debug builds if VerifyDependencies is enabled.
+                "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-VerifyDependencies",
+              //These don't seem necessary when pet-clinic is run in "Spring AOT" mode
+              //"--add-opens", "java.base/java.io=ALL-UNNAMED",
+              //"--add-opens", "java.base/java.lang=ALL-UNNAMED",
+              //"--add-opens", "java.rmi/sun.rmi.transport=ALL-UNNAMED"
+            };
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return cp;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            String cmdLine[] = new String[] {
+                "org.springframework.samples.petclinic.PetClinicApplication"
+            };
+            if (runMode == RunMode.PRODUCTION) {
+                // FIXME: bug JDK-8318393
+                cmdLine = StringArrayUtils.concat("-XX:-LoadCachedCode", cmdLine);
+            }
+
+            if (runMode.isProductionRun()) {
+                cmdLine = StringArrayUtils.concat("-Xlog:scc=error", cmdLine);
+            }
+            return cmdLine;
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (!runMode.isStaticDump()) {
+                out.shouldContain("Booted and returned in ");
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedPackages.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedPackages.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedPackages.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2024, 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
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ *          /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build ArchivedPackages
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar ArchivedPackagesApp pkg3.Package3A pkg3.Package3B
+ * @run driver ArchivedPackages STATIC
+ */
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+import pkg3.Package3A;
+import pkg3.Package3B;
+
+public class ArchivedPackages {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "ArchivedPackagesApp";
+
+    public static void main(String[] args) throws Exception {
+        Tester t = new Tester();
+        t.run(args);
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-Dcds.debug.archived.packages=true",
+                "-XX:+AOTClassLinking",
+                "-XX:+ArchivePackages"
+            };
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                mainClass,
+                runMode.name()
+            };
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (runMode.isStaticDump()) {
+                out.shouldContain("Archiving NamedPackage \"\" for jdk.internal.loader.ClassLoaders$AppClassLoader");
+                out.shouldContain("Archiving Package \"pkg3\" for jdk.internal.loader.ClassLoaders$AppClassLoader");
+                out.shouldContain("Archiving NamedPackage \"com.sun.tools.javac\" for jdk.internal.loader.ClassLoaders$AppClassLoader");
+            } else if (runMode.isProductionRun()) {
+                out.shouldContain(ArchivedPackagesApp.msg);
+            }
+        }
+    }
+}
+
+class ArchivedPackagesApp {
+    public static String msg = "Package for archived class equals to that of non-archived class";
+    public static void main(String args[]) throws Exception {
+        Class.forName("com.sun.tools.javac.Main");
+        Package x = Package3A.class.getPackage();
+        if (args[0].equals("PRODUCTION")) {
+            Package y = Package3B.class.getPackage();
+            System.out.println("Package for archived class: " + x);
+            System.out.println("Package for non-archived class: " + y);
+            if (x == y) {
+                System.out.println(msg);
+            } else {
+                throw new RuntimeException("Unexpected for archived package");
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedProtectionDomains.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedProtectionDomains.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedProtectionDomains.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2024, 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
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ *          /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build ArchivedProtectionDomains
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app1.jar ArchivedProtectionDomainsApp
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app2.jar pkg3.Package3A pkg3.Package3B
+ * @run driver ArchivedProtectionDomains STATIC
+ */
+
+import java.io.File;
+import java.security.ProtectionDomain;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.StringArrayUtils;
+
+import pkg3.Package3A;
+import pkg3.Package3B;
+
+public class ArchivedProtectionDomains {
+    static final String app1Jar = ClassFileInstaller.getJarPath("app1.jar");
+    static final String app2Jar = ClassFileInstaller.getJarPath("app2.jar");
+    static final String mainClass = "ArchivedProtectionDomainsApp";
+
+    public static void main(String[] args) throws Exception {
+        Tester t = new Tester();
+        t.run(new String[] {args[0]});
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return app1Jar + File.pathSeparator + app2Jar;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            String[] args = StringArrayUtils.concat(
+                "-Dcds.debug.archived.protection.domains=true",
+                "-XX:+AOTClassLinking",
+                "-XX:+ArchiveProtectionDomains",
+                "-Xlog:cds+protectiondomain");
+            return args;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                mainClass,
+                runMode.name()
+            };
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            assertArchived(out, runMode);
+        }
+
+        void assertArchived(OutputAnalyzer out, RunMode runMode) {
+            if (runMode.isStaticDump()) {
+                out.shouldMatch("Archiving ProtectionDomain .jrt:/jdk.compiler .* for jdk.internal.loader.ClassLoaders.AppClassLoader");
+                out.shouldMatch("Archiving ProtectionDomain .*app1.jar .* for jdk.internal.loader.ClassLoaders.AppClassLoader");
+                out.shouldMatch("Archiving ProtectionDomain .*app2.jar .* for jdk.internal.loader.ClassLoaders.AppClassLoader");
+            } else if (runMode.isProductionRun()) {
+                out.shouldContain("Archived protection domain for ArchivedProtectionDomainsApp = found");
+                out.shouldContain("Archived protection domain for pkg3.Package3A = found");
+                out.shouldContain("Archived protection domain for com.sun.tools.javac.Main = found");
+                out.shouldContain(ArchivedProtectionDomainsApp.msg);
+
+                // This class should not be archived.
+                out.shouldNotContain("Archived protection domain for pkg3.Package3B = found");
+                out.shouldNotContain("Archived protection domain for pkg3.Package3B = none");
+            }
+        }
+    }
+}
+
+class ArchivedProtectionDomainsApp {
+    public static String msg = "ProtectionDomain for archived class equals to that of non-archived class";
+    public static void main(String args[]) throws Exception {
+        Class jc = Class.forName("com.sun.tools.javac.Main");
+        ProtectionDomain x = Package3A.class.getProtectionDomain();
+        if (args[0].equals("PRODUCTION")) {
+            ProtectionDomain y = Package3B.class.getProtectionDomain();
+            System.out.println("ProtectionDomain for com.sun.tools.javac.Main: " + jc.getProtectionDomain());
+            System.out.println("ProtectionDomain for archived class: " + x);
+            System.out.println("ProtectionDomain for non-archived class: " + y);
+            if (x == y) {
+                System.out.println(msg);
+            } else {
+                throw new RuntimeException("Unexpected for archived package");
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyAndJavac.java b/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyAndJavac.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyAndJavac.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023, 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
+ * @bug 8293336
+ * @summary Test for archiving resolved invokedynamic call sites (with JavaC)
+ * @requires vm.cds.write.archived.java.heap
+ * @modules java.base/sun.invoke.util java.logging
+ * @library /test/jdk/lib/testlibrary /test/lib
+ *          /test/hotspot/jtreg/runtime/cds/appcds
+ *          /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build IndyAndJavac
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Dummy
+ * @run driver IndyAndJavac
+ */
+
+
+import java.io.FileWriter;
+
+import jdk.test.lib.helpers.ClassFileInstaller;
+
+public class IndyAndJavac extends IndyTestBase {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass_javac = "com.sun.tools.javac.Main";
+
+    // Force some tests to be disabled during development.
+    static String forceSkip = null;
+
+    public static void main(String[] args) throws Exception {
+        setup(forceSkip, appJar);
+        testJavaC();
+    }
+
+    static void testJavaC() throws Exception {
+        String sourceFile = "Test.java";
+        try (FileWriter fw = new FileWriter(sourceFile)) {
+            fw.write("public class Test {\n");
+            for (int i = 0; i < 3000; i++) {
+                fw.write("    private static final int arr_" + i + "[] = {1, 2, 3};\n");
+                fw.write("    private static int method_" + i + "() { return arr_" + i + "[1];}\n");
+            }
+            fw.write("}\n");
+        }
+        test("Use javac with archived MethodTypes and LambdaForms", mainClass_javac, sourceFile, sourceFile,
+             RUN_STATIC |
+           //RUN_BENCH |
+             RUN_AOT
+             );
+        checkExec(null, /*lambdaFormsMustBeArchived*/false); // Some lambda forms are generated because we don't cache invokedynamic for lambda proxies yet
+    }
+}
+
+class Dummy {
+
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyStringConcat.java b/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyStringConcat.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyStringConcat.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2023, 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
+ * @bug 8293336
+ * @summary Test for archiving resolved invokedynamic call sites
+ * @requires vm.cds.write.archived.java.heap
+ * @modules java.base/sun.invoke.util java.logging
+ * @library /test/jdk/lib/testlibrary /test/lib
+ *          /test/hotspot/jtreg/runtime/cds/appcds
+ *          /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build IndyStringConcat
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar ConcatA ConcatA$DummyClass ConcatB
+ * @run driver IndyStringConcat
+ */
+
+
+// NOTE: to run a subset of the tests, use something like
+//
+// jtreg .... -vmoptions:-DIndyStringConcat.test.only='(1)|(2)' \
+//            -vmoptions:-DIndyStringConcat.test.skip='2' IndyStringConcat.java
+//
+// A regexp can be specified in these two properties. Note that the specified regexp must be a full match.
+// E.g., -DIndyStringConcat.test.only='1.*' matches "1" and "12", but does NOT match "21".
+// (Search for ")$" in the code below)
+//
+// Also, some tests may be forced to be skipped. To run them, edit the variable forceSkip below.
+
+import jdk.test.lib.helpers.ClassFileInstaller;
+
+public class IndyStringConcat extends IndyTestBase {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass_ConcatA = ConcatA.class.getName();
+    static final String mainClass_ConcatB = ConcatB.class.getName();
+
+    // Force some tests to be disabled during development.
+    static String forceSkip = null;
+
+    public static void main(String[] args) throws Exception {
+        setup(forceSkip, appJar);
+
+        // ------------------------------------------------------------
+        test("\"LIT\" + (String)b", mainClass_ConcatA, "a");
+        checkExec("LIT222");
+
+        // ------------------------------------------------------------
+        test("(String)a + (String)b", mainClass_ConcatA, "b");
+        checkExec("aaa222");
+
+        // ------------------------------------------------------------
+        test("(String)a + (int)b", mainClass_ConcatA, "c");
+        checkExec("aaa333");
+
+        // ------------------------------------------------------------
+        test("Test with ConcatB", mainClass_ConcatB, "B1");
+        checkExec("ConcatBLIT333");
+
+        // ------------------------------------------------------------
+        test("Run ConcatB.foo() without dump-time resolution of its invokedynamic callsite", mainClass_ConcatB, "", "B1");
+        checkExec("ConcatBLIT333", /* lambdaFormsMustBeArchived*/ false);
+
+        // ------------------------------------------------------------
+        test("WithAOT (no loop) for \"LIT\" + (String)b", mainClass_ConcatA, "a", "a",  RUN_AOT);
+        checkExec("LIT222");
+        shouldUseDynamicArchive();
+
+        // ------------------------------------------------------------
+        test("WithAOT (with loop) for \"LIT\" + (String)b", mainClass_ConcatA, "loopa", "loopa",  RUN_STATIC | RUN_AOT);
+        checkExec("LITL");
+        shouldUseDynamicArchive();
+    }
+
+    static void shouldUseDynamicArchive() throws Exception {
+        shouldMatch("Opened archive IndyStringConcat-[0-9]+-dyn.jsa");
+    }
+}
+
+class ConcatA {
+    public static void main(String args[]) throws Exception {
+        if (args[0].equals("a")) {
+            foo("222");
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        } else if (args[0].equals("b")) {
+            bar("aaa", "222");
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        } else if (args[0].equals("c")) {
+            baz("aaa", 333);
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        } else if (args[0].equals("loopa")) {
+            loopa();
+            loopa();
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        }
+
+        if (args.length > 1 && args[1].equals("load-extra-class")) {
+            // Work around "There is no class to be included in the dynamic archive." problem, where the
+            // dynamic archive is not generated.
+            DummyClass.doit();
+        }
+    }
+
+    static void loopa() {
+        for (int i = 0; i < 100000; i++) {
+            foo("L");
+        }
+    }
+
+    static String x;
+    static void foo(String b) {
+        x = "LIT" + b;
+    }
+    static void bar(String a, String b) {
+        x = a + b;
+    }
+    static void baz(String a, int b) {
+        x = a + b;
+    }
+
+    static class DummyClass {
+        static void doit() {}
+    }
+}
+
+
+class ConcatB {
+    public static void main(String args[]) throws Exception {
+        if (args[0].equals("B1")) {
+            foo("333");
+            System.out.print("OUTPUT = ");
+            System.out.println(x); // Avoid using + in diagnostic output.
+        }
+    }
+
+    static String x;
+    static void foo(String b) {
+        x = "ConcatBLIT" + b;
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyTestBase.java b/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyTestBase.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/indy/IndyTestBase.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2023, 2024, 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.
+ *
+ */
+
+import java.io.FileWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+
+public class IndyTestBase {
+    static Pattern testOnlyPattern = null; // matches testNumber
+    static Pattern testSkipPattern = null; // matches testNumber    
+    static Pattern forceSkipPattern = null; // Force some tests to be disabled during development.
+    static int testNumber = 0;
+    static String appJar;
+    static OutputAnalyzer output = null; // after test() returns, this is the output of the final run
+    static OutputAnalyzer staticDumpOutput = null;
+    static String jtregTestCase = "testcase";
+
+    // bits for runMode
+    static final int RUN_STATIC = 1;   // Run with static archive only (no AOT)
+    static final int RUN_AOT    = 2;   // Run with AOT (which uses dynamic archive)
+    static final int RUN_BENCH  = 4;   // Run in benchmark mode (no logs)
+
+    static void setup(String appJar) throws Exception {
+      setup(null, appJar);
+    }
+
+    static void setup(String forceSkip, String appJar) throws Exception {
+        String testName = System.getProperty("test.name");
+        if (testName != null) {
+            Path p = Paths.get(testName);
+            String file = p.getFileName().toString();
+            int i = file.indexOf(".");
+            jtregTestCase = file.substring(0, i);
+        }
+        IndyTestBase.appJar = appJar;
+        String testOnly = System.getProperty(jtregTestCase + ".test.only");
+        if (testOnly != null) {
+            testOnlyPattern = Pattern.compile("^(" + testOnly + ")$");
+        }
+        String testSkip = System.getProperty(jtregTestCase + ".test.skip");
+        if (testSkip != null) {
+            testSkipPattern = Pattern.compile("^(" + testSkip + ")$");
+        }
+
+        if (forceSkip != null) {
+            forceSkipPattern = Pattern.compile("^(" + forceSkip + ")$");
+        }
+    }
+
+    static boolean shouldTest(String s) {
+        if (testOnlyPattern != null) {
+            Matcher matcher = testOnlyPattern.matcher(s);
+            if (!matcher.find()) {
+                return false;
+            }
+        }
+        if (testSkipPattern != null) {
+            Matcher matcher = testSkipPattern.matcher(s);
+            if (matcher.find()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    // Run the test program with the same arg for both training run and production run
+    static void test(String testNote, String mainClass, String arg) throws Exception {
+        test(testNote, mainClass, arg, arg, RUN_STATIC);
+    }
+
+    static void test(String testNote, String mainClass, String trainingArg, String productionArg) throws Exception {
+        test(testNote, mainClass, trainingArg, productionArg, RUN_STATIC);
+    }
+
+    static void test(String testNote, String mainClass, String trainingArg, String productionArg, int runMode) throws Exception {
+        output = null;
+        testNumber ++;
+        String skipBy = null;
+
+        if (forceSkipPattern != null) {
+            Matcher matcher = forceSkipPattern.matcher(testNote);
+            if (matcher.find()) {
+                skipBy = " ***** (hard coded) Skipped by test note";
+            }
+        }
+        if (skipBy == null && !shouldTest("" + testNumber)) {
+            skipBy = " ***** Skipped by test number";
+        }
+
+        if (skipBy != null) {
+            System.out.println("         Test : #" + testNumber + ", " + testNote + skipBy);
+            return;
+        }
+
+        System.out.println("==================================================================");
+        System.out.println("         Test : #" + testNumber + ", " + testNote);
+        System.out.println("  trainingArg : " + trainingArg);
+        System.out.println("productionArg : " + productionArg);
+        System.out.println("      runMode : " + runModeString(runMode));
+        System.out.println("vvvv==========================================================vvvv");
+        String s = jtregTestCase + "-" + testNumber;
+        String classList = s + ".classlist";
+        String archiveName = s + ".jsa";
+
+        // Create classlist
+        System.out.println("#" + testNumber + ": Create classlist");
+        CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass, trainingArg);
+
+        // Dump static archive
+        System.out.println("#" + testNumber + ": Dump static archive");
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-XX:SharedClassListFile=" + classList,
+                       "-XX:+AOTClassLinking",  // by default enables AOTInvokeDynamicLinking
+                       "-XX:+ArchiveDynamicProxies",
+                       "-XX:+ArchiveReflectionData",
+                       "-cp", appJar,
+                       "-Xlog:cds+heap",
+                       "-Xlog:cds+resolve=trace",
+                       "-Xlog:cds,cds+class=debug")
+            .setArchiveName(archiveName);
+        staticDumpOutput = CDSTestUtils.createArchiveAndCheck(opts);
+        TestCommon.checkExecReturn(staticDumpOutput, 0, true);
+
+        if ((runMode & RUN_STATIC) != 0) {
+            // Run with static archive
+            System.out.println("#" + testNumber + ": Run with static archive (no AOT)");
+            CDSOptions runOpts = (new CDSOptions())
+                .addPrefix("-cp", appJar)
+                .setArchiveName(archiveName)
+                .setUseVersion(false)
+                .addSuffix(mainClass)
+                .addSuffix(productionArg);
+            if ((runMode & RUN_BENCH) != 0) {
+                runOpts.setBenchmarkMode(true);
+            } else {
+                runOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:cds+heap=debug",
+                                  "-Xlog:methodhandles");
+            }
+            output = CDSTestUtils.runWithArchive(runOpts);
+            TestCommon.checkExecReturn(output, 0, true);
+        }
+
+        if ((runMode & RUN_AOT) != 0) {
+            System.out.println("#" + testNumber +
+                               ": (STEP 3 of 5) Run with static archive and dump profile in dynamic archive (With Training Data Replay)");
+            String dynamicArchiveName = s + "-dyn.jsa";
+            CDSOptions dynDumpOpts = (new CDSOptions())
+                .addPrefix("-cp", appJar)
+                .setArchiveName(archiveName)
+                .setUseVersion(false)
+                .addSuffix("-XX:ArchiveClassesAtExit=" + dynamicArchiveName)
+                .addSuffix("-XX:-BackgroundCompilation")
+                .addSuffix("-XX:+RecordTraining");
+            if ((runMode & RUN_BENCH) != 0) {
+                dynDumpOpts.setBenchmarkMode(true);
+                dynDumpOpts.addPrefix("-Xlog:cds=debug", "-Xlog:scc");
+            } else {
+                dynDumpOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug",
+                                      "-Xlog:cds+class=debug");
+            }
+            // The main class name and arguments
+            dynDumpOpts
+                .addSuffix(mainClass)
+                .addSuffix(productionArg);
+            output = CDSTestUtils.runWithArchive(dynDumpOpts);
+            TestCommon.checkExecReturn(output, 0, true);
+
+            //======================================================================
+            System.out.println("#" + testNumber +
+                               ": (STEP 4 of 5) Run with dynamic archive and generate AOT code");
+            String sharedCodeArchive = s + ".scc";
+            CDSOptions aotOpts = (new CDSOptions())
+                .addPrefix("-cp", appJar)
+                .setArchiveName(archiveName)
+                .setUseVersion(false)
+                .addSuffix("-XX:-BackgroundCompilation")
+                .addSuffix("-XX:+StoreCachedCode")
+                .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive)
+                .addSuffix("-XX:CachedCodeMaxSize=100M");
+
+            if ((runMode & RUN_BENCH) != 0) {
+                aotOpts.setBenchmarkMode(true);
+                aotOpts.addPrefix("-Xlog:cds=debug", "-Xlog:scc");
+            } else {
+                aotOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:scc+init,scc+exit");
+                if (mainClass.equals("ConcatA")) {
+                    // Hard-code the printing of loopA for now
+                    dynDumpOpts
+                        .addSuffix("-XX:CompileCommand=print,*::loopA")
+                        .addSuffix("-XX:+PrintAssembly");
+                }
+            }
+            // The main class name and arguments
+            aotOpts
+                .addSuffix(mainClass)
+                .addSuffix(productionArg);
+            output = CDSTestUtils.runWithArchive(aotOpts);
+            TestCommon.checkExecReturn(output, 0, true);
+
+            //======================================================================
+            System.out.println("#" + testNumber + ": (STEP 5 of 5) Run with dynamic archive and AOT cache");
+            CDSOptions runOpts = (new CDSOptions())
+                .addPrefix("-cp", appJar)
+                .setArchiveName(dynamicArchiveName)
+                .addSuffix("-XX:+LoadCachedCode")
+                .addSuffix("-XX:CachedCodeFile=" + sharedCodeArchive)
+                .setUseVersion(false)
+                .addSuffix(mainClass)
+                .addSuffix(productionArg);
+
+            if ((runMode & RUN_BENCH) != 0) {
+                runOpts.setBenchmarkMode(true);
+            } else {
+                // Tell CDSTestUtils to not add the -XX:VerifyArchivedFields=1 flag, which seems to be causing a crash
+                // in the AOT code.
+                runOpts.setBenchmarkMode(true);
+
+                runOpts.addPrefix("-Xlog:class+load", "-Xlog:cds=debug", "-Xlog:scc+init");
+                if (mainClass.equals("ConcatA")) {
+                    // Hard-code the printing of loopA for now
+                    dynDumpOpts
+                        .addSuffix("-XX:CompileCommand=print,*::loopA")
+                        .addSuffix("-XX:+PrintAssembly");
+                }
+            }
+
+            output = CDSTestUtils.runWithArchive(runOpts);
+            TestCommon.checkExecReturn(output, 0, true);
+        }
+    }
+
+    static void checkExec(String expectedOutput) throws Exception {
+        checkExec(expectedOutput, /* lambdaFormsMustBeArchived*/ true);
+    }
+
+    static void checkExec(String expectedOutput, boolean lambdaFormsMustBeArchived)  throws Exception {
+        if (output == null) { // test may be skipped
+            return;
+        }
+        if (expectedOutput != null) {
+            TestCommon.checkExecReturn(output, 0, true, "OUTPUT = " + expectedOutput);
+        }
+        if (lambdaFormsMustBeArchived) {
+            output.shouldMatch("LambdaForm[$]((MH)|(DMH))/0x[0-9]+ source: shared objects file");
+            output.shouldNotMatch("LambdaForm[$]MH/0x[0-9]+ source: __JVM_LookupDefineClass__");
+            output.shouldNotMatch("LambdaForm[$]DMH/0x[0-9]+ source: __JVM_LookupDefineClass__");
+        }
+    }
+
+    static void shouldMatch(String pattern) throws Exception {
+        if (output != null) { // test may be skipped
+            output.shouldMatch(pattern);
+        }
+    }
+
+    static String runModeString(int runMode) {
+        String prefix = "";
+        String s = "";
+        if ((runMode & RUN_STATIC) != 0) {
+            s += "RUN_STATIC";
+            prefix = " | ";
+        }
+        if ((runMode & RUN_AOT) != 0) {
+            s += prefix;
+            s += "RUN_AOT";
+            prefix = " | ";
+        }
+        return s;
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/indy/StringConcatStress.java b/test/hotspot/jtreg/runtime/cds/appcds/indy/StringConcatStress.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/indy/StringConcatStress.java
@@ -0,0 +1,3093 @@
+/*
+ * Copyright (c) 2023, 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
+ * @bug 8293336
+ * @summary Test for archiving resolved invokedynamic call sites (with JavaC)
+ * @requires vm.cds.write.archived.java.heap
+ * @modules java.base/sun.invoke.util java.logging
+ * @library /test/jdk/lib/testlibrary /test/lib
+ *          /test/hotspot/jtreg/runtime/cds/appcds
+ *          /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build StringConcatStress
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar StringConcatStressApp
+ * @run driver StringConcatStress
+ */
+
+import jdk.test.lib.helpers.ClassFileInstaller;
+
+public class StringConcatStress extends IndyTestBase {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "StringConcatStressApp";
+
+    public static void main(String[] args) throws Exception {
+        setup(appJar);
+        test("Lots and lots of string concats", mainClass, "check", "check", RUN_STATIC | RUN_AOT | RUN_BENCH);
+    }
+}
+
+class StringConcatStressApp {
+    static String result;
+    public static void main(String... args) throws Exception {
+        long maxElapseMs = 2000;
+        // Run this test case for no more than maxElapseMs milliseconds
+        String s = System.getProperty("StringConcatStress.max.ms");
+        if (s != null) {
+            maxElapseMs = Long.parseLong(s);
+        }
+
+        long start = System.currentTimeMillis();
+        int count = 0;
+        long end;
+
+        do {
+            count ++;
+            test();
+            end = System.currentTimeMillis();;
+        } while ((end - start) < maxElapseMs);
+
+        if (args.length > 0 && args[0].equals("check")) {
+            System.out.println("Result  = " + result);
+            System.out.println("Elapsed = " + (end - start) + " ms");
+            System.out.println("Count   = " + count);
+        }
+    }
+
+    static void test() {
+        int i = 17;
+        byte b = (byte)127;
+        char c = 'c';
+        long l = 4711;
+        boolean z = true;
+        short s = 5;
+        float f = 1.0f;
+        double d = 2.5;
+        String S = "S";
+        String concat;
+        concat = "" + i + l + i + b + d + d + d + b;
+        concat = "" + S + S + f + f + S + l + "S" + f;
+        concat = "" + c + f + s + s + "S" + d + s + b;
+        concat = "" + f + z + z + s + l + d + c + S;
+        concat = "" + f + "S" + c + b + f + c + "S" + s;
+        concat = "" + "S" + d + l + s + s + z + i + b;
+        concat = "" + S + b + "S" + s + S + l + d + s;
+        concat = "" + d + b + s + z + i + l + z + S;
+        concat = "" + z + z + d + i + c + c + d + "S";
+        concat = "" + l + "S" + z + l + z + z + "S" + b;
+        concat = "" + f + d + b + S + "S" + "S" + l + l;
+        concat = "" + f + b + l + c + f + f + s + S;
+        concat = "" + "S" + z + i + d + i + b + l + "S";
+        concat = "" + "S" + S + S + z + i + S + z + c;
+        concat = "" + f + i + i + b + z + c + i + S;
+        concat = "" + d + f + d + f + s + i + d + "S";
+        concat = "" + s + i + b + z + s + b + "S" + f;
+        concat = "" + z + z + i + "S" + d + "S" + "S" + l;
+        concat = "" + i + c + d + i + s + "S" + "S" + d;
+        concat = "" + z + i + z + z + S + d + b + f;
+        concat = "" + b + S + b + s + z + z + S + f;
+        concat = "" + S + b + i + l + S + d + s + S;
+        concat = "" + c + S + c + f + l + "S" + S + "S";
+        concat = "" + b + l + b + b + s + l + l + z;
+        concat = "" + "S" + b + c + d + f + l + l + b;
+        concat = "" + c + S + S + i + c + z + d + z;
+        concat = "" + z + d + b + S + "S" + i + l + b;
+        concat = "" + z + l + S + f + b + d + l + i;
+        concat = "" + i + b + l + b + l + "S" + d + d;
+        concat = "" + c + s + c + z + i + i + c + c;
+        concat = "" + l + i + z + "S" + d + "S" + "S" + l;
+        concat = "" + "S" + "S" + b + c + i + S + z + f;
+        concat = "" + d + l + d + i + d + c + z + f;
+        concat = "" + b + "S" + c + c + f + z + b + f;
+        concat = "" + s + c + d + c + "S" + b + "S" + b;
+        concat = "" + i + i + S + i + s + d + f + d;
+        concat = "" + S + c + s + i + "S" + c + s + b;
+        concat = "" + b + s + l + S + S + s + c + i;
+        concat = "" + s + S + d + f + S + S + s + z;
+        concat = "" + S + S + c + "S" + z + f + l + s;
+        concat = "" + c + d + z + f + s + f + c + S;
+        concat = "" + z + b + c + c + S + c + z + l;
+        concat = "" + l + "S" + "S" + c + i + S + c + l;
+        concat = "" + d + d + i + z + "S" + z + i + d;
+        concat = "" + d + d + f + i + "S" + b + d + b;
+        concat = "" + c + l + f + d + d + b + d + c;
+        concat = "" + "S" + "S" + i + i + f + c + f + S;
+        concat = "" + l + z + S + b + d + f + d + b;
+        concat = "" + l + S + z + f + "S" + l + f + "S";
+        concat = "" + c + b + i + S + s + f + d + b;
+        concat = "" + s + l + S + b + i + l + z + f;
+        concat = "" + c + b + c + c + c + c + b + "S";
+        concat = "" + s + s + l + S + "S" + z + l + f;
+        concat = "" + i + d + d + z + z + b + b + s;
+        concat = "" + "S" + "S" + b + d + z + f + s + s;
+        concat = "" + b + "S" + c + b + z + d + "S" + l;
+        concat = "" + c + f + S + s + z + s + S + i;
+        concat = "" + d + b + i + i + z + S + c + z;
+        concat = "" + s + l + l + f + "S" + f + s + S;
+        concat = "" + i + c + l + "S" + S + s + f + f;
+        concat = "" + d + d + s + "S" + l + z + f + b;
+        concat = "" + f + l + c + "S" + f + f + "S" + S;
+        concat = "" + f + l + d + S + c + i + f + c;
+        concat = "" + c + "S" + z + z + i + s + i + S;
+        concat = "" + f + "S" + l + d + c + s + s + b;
+        concat = "" + i + i + "S" + l + c + S + d + b;
+        concat = "" + "S" + z + s + f + "S" + d + z + z;
+        concat = "" + "S" + S + l + "S" + l + "S" + d + f;
+        concat = "" + l + f + S + f + "S" + f + i + b;
+        concat = "" + l + c + b + f + s + S + d + c;
+        concat = "" + s + c + l + "S" + l + b + l + S;
+        concat = "" + z + "S" + s + "S" + z + s + i + c;
+        concat = "" + i + z + "S" + z + f + i + S + S;
+        concat = "" + S + b + f + "S" + f + i + b + S;
+        concat = "" + s + S + b + S + c + c + f + b;
+        concat = "" + "S" + d + b + d + i + c + S + c;
+        concat = "" + b + "S" + f + c + s + b + z + "S";
+        concat = "" + b + z + l + l + s + l + l + S;
+        concat = "" + "S" + "S" + S + S + "S" + c + i + S;
+        concat = "" + l + S + s + f + l + s + s + "S";
+        concat = "" + z + d + S + z + b + l + l + b;
+        concat = "" + S + f + b + "S" + c + d + f + d;
+        concat = "" + i + l + S + c + S + d + "S" + c;
+        concat = "" + c + b + "S" + s + s + i + z + "S";
+        concat = "" + f + S + c + i + i + l + z + d;
+        concat = "" + z + z + z + l + f + S + f + b;
+        concat = "" + i + f + z + l + s + S + b + l;
+        concat = "" + z + c + b + S + l + S + s + s;
+        concat = "" + "S" + b + i + l + b + f + c + l;
+        concat = "" + b + c + s + S + f + l + S + i;
+        concat = "" + d + c + l + i + b + "S" + f + f;
+        concat = "" + c + c + "S" + c + s + S + i + c;
+        concat = "" + l + "S" + d + b + c + s + b + d;
+        concat = "" + f + z + f + l + "S" + f + l + f;
+        concat = "" + z + l + "S" + b + f + c + z + b;
+        concat = "" + z + "S" + c + i + b + b + "S" + S;
+        concat = "" + f + "S" + f + s + "S" + f + b + "S";
+        concat = "" + f + i + c + S + b + "S" + d + d;
+        concat = "" + f + f + d + i + f + d + d + i;
+        concat = "" + f + d + f + z + f + c + s + b;
+        concat = "" + f + b + f + s + f + f + c + S;
+        concat = "" + i + S + l + c + s + d + d + f;
+        concat = "" + S + b + c + d + c + s + b + z;
+        concat = "" + f + b + "S" + z + l + f + s + b;
+        concat = "" + d + S + f + "S" + f + s + f + c;
+        concat = "" + c + d + "S" + i + d + z + i + c;
+        concat = "" + S + "S" + c + S + c + l + c + i;
+        concat = "" + d + c + f + "S" + b + S + s + d;
+        concat = "" + i + S + z + i + z + l + i + c;
+        concat = "" + S + f + s + f + z + i + "S" + f;
+        concat = "" + b + d + s + d + d + i + S + z;
+        concat = "" + z + z + b + i + d + z + s + c;
+        concat = "" + c + i + b + z + s + c + l + c;
+        concat = "" + S + f + s + c + s + z + i + "S";
+        concat = "" + z + "S" + l + c + b + S + S + S;
+        concat = "" + f + S + b + l + c + s + c + i;
+        concat = "" + d + s + S + "S" + "S" + z + b + c;
+        concat = "" + d + c + l + i + d + "S" + z + l;
+        concat = "" + "S" + S + z + s + d + i + z + "S";
+        concat = "" + c + z + f + "S" + l + i + c + i;
+        concat = "" + c + i + c + S + s + s + S + z;
+        concat = "" + c + f + f + l + l + i + z + d;
+        concat = "" + s + S + "S" + z + l + "S" + S + c;
+        concat = "" + f + l + z + f + c + i + c + z;
+        concat = "" + z + s + f + l + i + i + "S" + i;
+        concat = "" + S + i + i + b + S + s + z + z;
+        concat = "" + z + "S" + "S" + z + f + "S" + i + f;
+        concat = "" + f + "S" + c + c + i + z + l + f;
+        concat = "" + b + f + f + S + f + z + S + s;
+        concat = "" + i + d + f + z + d + i + i + f;
+        concat = "" + b + d + d + l + "S" + d + b + i;
+        concat = "" + i + "S" + b + i + S + l + z + f;
+        concat = "" + z + f + s + s + "S" + s + l + "S";
+        concat = "" + l + c + s + z + s + b + c + s;
+        concat = "" + s + f + s + b + z + "S" + z + f;
+        concat = "" + b + i + z + i + l + "S" + i + S;
+        concat = "" + z + f + S + d + d + b + f + i;
+        concat = "" + l + i + S + i + z + s + i + S;
+        concat = "" + c + f + f + c + S + "S" + d + c;
+        concat = "" + c + f + d + "S" + l + c + c + s;
+        concat = "" + s + c + c + f + b + f + i + "S";
+        concat = "" + i + "S" + S + l + i + "S" + i + l;
+        concat = "" + b + l + z + "S" + s + c + z + i;
+        concat = "" + d + "S" + "S" + "S" + b + S + c + l;
+        concat = "" + l + l + z + "S" + z + d + S + b;
+        concat = "" + S + b + s + b + b + i + d + s;
+        concat = "" + "S" + c + S + z + s + i + z + b;
+        concat = "" + f + "S" + c + "S" + s + l + c + b;
+        concat = "" + l + s + c + S + z + z + d + s;
+        concat = "" + z + b + s + s + l + d + f + i;
+        concat = "" + c + c + i + c + b + c + S + s;
+        concat = "" + z + f + s + i + c + f + b + i;
+        concat = "" + "S" + f + f + f + z + l + l + l;
+        concat = "" + c + i + z + S + d + l + S + z;
+        concat = "" + c + d + "S" + l + f + c + l + "S";
+        concat = "" + "S" + c + b + z + c + c + c + "S";
+        concat = "" + z + d + S + d + l + d + c + z;
+        concat = "" + c + d + l + s + z + s + b + f;
+        concat = "" + l + s + d + z + l + b + i + i;
+        concat = "" + f + l + b + i + l + b + z + "S";
+        concat = "" + c + b + c + l + S + i + d + c;
+        concat = "" + d + s + s + b + b + s + s + i;
+        concat = "" + d + i + S + z + l + b + b + d;
+        concat = "" + "S" + "S" + c + "S" + f + b + z + f;
+        concat = "" + z + d + "S" + z + "S" + S + d + b;
+        concat = "" + b + i + z + b + S + s + c + l;
+        concat = "" + S + z + c + z + b + d + s + i;
+        concat = "" + S + "S" + z + s + l + z + s + s;
+        concat = "" + f + l + f + f + b + f + z + d;
+        concat = "" + z + z + z + f + i + l + f + b;
+        concat = "" + i + l + z + i + s + d + f + l;
+        concat = "" + "S" + "S" + f + l + "S" + i + "S" + l;
+        concat = "" + S + b + z + f + "S" + l + i + i;
+        concat = "" + z + c + d + z + l + d + z + z;
+        concat = "" + l + z + d + S + l + f + S + d;
+        concat = "" + S + i + d + b + "S" + f + z + z;
+        concat = "" + f + d + "S" + b + l + f + b + b;
+        concat = "" + b + S + "S" + d + "S" + s + s + f;
+        concat = "" + z + c + b + b + z + S + s + b;
+        concat = "" + l + f + S + S + c + f + c + z;
+        concat = "" + i + S + l + d + f + "S" + S + s;
+        concat = "" + i + c + d + z + i + c + d + l;
+        concat = "" + l + d + d + S + l + b + S + i;
+        concat = "" + c + "S" + b + z + l + i + z + S;
+        concat = "" + z + i + z + s + d + S + i + S;
+        concat = "" + z + f + l + "S" + s + b + z + i;
+        concat = "" + z + l + z + f + b + c + d + "S";
+        concat = "" + l + s + i + "S" + S + c + f + i;
+        concat = "" + S + s + b + f + f + "S" + s + i;
+        concat = "" + i + s + c + "S" + z + f + "S" + d;
+        concat = "" + f + s + c + f + d + s + b + c;
+        concat = "" + c + s + d + d + "S" + s + f + "S";
+        concat = "" + "S" + c + z + c + s + s + S + S;
+        concat = "" + S + S + d + s + d + l + s + b;
+        concat = "" + i + s + b + S + S + b + l + i;
+        concat = "" + c + b + b + b + f + s + f + z;
+        concat = "" + z + "S" + s + d + f + i + i + z;
+        concat = "" + S + S + c + i + z + f + i + s;
+        concat = "" + "S" + "S" + S + z + z + "S" + s + f;
+        concat = "" + b + "S" + S + l + i + d + S + z;
+        concat = "" + s + c + "S" + S + b + i + i + i;
+        concat = "" + s + z + i + c + s + l + S + f;
+        concat = "" + d + f + S + S + b + z + c + "S";
+        concat = "" + z + "S" + c + "S" + b + s + f + "S";
+        concat = "" + S + z + s + "S" + b + f + d + S;
+        concat = "" + l + i + b + b + "S" + "S" + l + z;
+        concat = "" + S + l + f + i + i + f + z + f;
+        concat = "" + S + "S" + s + l + b + d + d + s;
+        concat = "" + d + "S" + s + z + c + "S" + i + l;
+        concat = "" + b + s + f + f + z + "S" + d + d;
+        concat = "" + z + f + c + b + f + c + "S" + "S";
+        concat = "" + S + S + z + b + s + f + l + i;
+        concat = "" + l + i + c + z + b + b + S + b;
+        concat = "" + z + c + i + "S" + s + s + "S" + S;
+        concat = "" + S + d + c + z + b + s + i + i;
+        concat = "" + s + z + d + s + i + d + d + S;
+        concat = "" + s + z + s + l + s + l + s + z;
+        concat = "" + f + d + c + f + l + f + i + z;
+        concat = "" + l + d + d + z + d + "S" + z + z;
+        concat = "" + "S" + d + c + "S" + l + s + "S" + c;
+        concat = "" + c + S + z + i + "S" + i + i + z;
+        concat = "" + d + "S" + S + d + z + "S" + f + c;
+        concat = "" + z + d + S + l + i + z + i + l;
+        concat = "" + d + l + c + d + f + s + f + "S";
+        concat = "" + f + c + d + d + l + l + f + d;
+        concat = "" + "S" + z + f + b + d + c + "S" + s;
+        concat = "" + l + S + "S" + d + S + i + l + z;
+        concat = "" + i + S + l + i + l + z + d + f;
+        concat = "" + d + i + s + S + l + d + S + S;
+        concat = "" + l + d + l + b + b + i + z + S;
+        concat = "" + f + s + z + f + i + l + d + b;
+        concat = "" + s + d + l + z + b + b + c + i;
+        concat = "" + f + z + s + d + f + f + d + f;
+        concat = "" + d + z + d + f + "S" + "S" + s + z;
+        concat = "" + c + f + "S" + "S" + b + s + s + i;
+        concat = "" + "S" + "S" + d + b + d + "S" + i + b;
+        concat = "" + d + S + l + f + d + d + b + b;
+        concat = "" + d + l + d + S + b + "S" + f + d;
+        concat = "" + l + c + s + l + "S" + l + S + f;
+        concat = "" + i + b + i + d + s + s + f + c;
+        concat = "" + i + z + z + S + d + z + c + l;
+        concat = "" + S + i + l + "S" + f + f + c + b;
+        concat = "" + "S" + S + d + b + d + f + z + d;
+        concat = "" + d + d + "S" + d + c + S + "S" + b;
+        concat = "" + l + i + c + f + S + s + z + z;
+        concat = "" + c + l + S + i + S + s + z + i;
+        concat = "" + i + "S" + i + "S" + "S" + "S" + b + c;
+        concat = "" + f + z + S + f + c + l + b + i;
+        concat = "" + d + l + b + z + d + s + s + S;
+        concat = "" + s + S + d + s + z + l + S + z;
+        concat = "" + i + s + z + b + l + l + b + d;
+        concat = "" + i + z + b + z + d + S + f + l;
+        concat = "" + S + z + l + b + i + f + l + i;
+        concat = "" + s + s + b + c + s + f + c + f;
+        concat = "" + f + i + s + f + c + S + c + s;
+        concat = "" + s + "S" + z + d + f + S + b + S;
+        concat = "" + i + z + S + d + l + b + i + z;
+        concat = "" + i + c + i + b + i + l + d + f;
+        concat = "" + S + b + s + s + s + c + S + l;
+        concat = "" + S + f + s + "S" + l + l + s + c;
+        concat = "" + z + l + S + S + s + d + S + f;
+        concat = "" + z + f + S + i + d + d + S + b;
+        concat = "" + S + d + "S" + l + l + i + l + i;
+        concat = "" + b + b + i + i + d + i + "S" + s;
+        concat = "" + l + c + "S" + l + s + b + l + l;
+        concat = "" + "S" + s + l + c + b + c + s + S;
+        concat = "" + z + S + b + d + l + d + i + b;
+        concat = "" + b + i + S + i + l + i + f + b;
+        concat = "" + d + S + s + S + "S" + b + c + l;
+        concat = "" + b + c + c + f + S + b + c + d;
+        concat = "" + i + i + b + S + f + f + b + c;
+        concat = "" + i + S + l + "S" + f + f + S + f;
+        concat = "" + f + l + f + c + "S" + c + b + "S";
+        concat = "" + S + S + z + z + "S" + d + s + "S";
+        concat = "" + b + z + i + i + f + c + s + l;
+        concat = "" + i + d + S + b + l + "S" + l + f;
+        concat = "" + b + c + s + s + z + f + z + l;
+        concat = "" + f + f + S + s + z + d + f + s;
+        concat = "" + S + "S" + d + f + c + S + z + i;
+        concat = "" + s + "S" + S + d + s + i + i + "S";
+        concat = "" + l + z + f + d + z + l + c + i;
+        concat = "" + f + i + S + S + c + i + i + d;
+        concat = "" + f + d + z + "S" + s + s + z + l;
+        concat = "" + c + s + s + S + z + z + b + d;
+        concat = "" + f + b + d + S + c + f + l + d;
+        concat = "" + "S" + d + s + f + "S" + s + c + b;
+        concat = "" + S + d + b + d + z + d + z + b;
+        concat = "" + s + l + c + z + s + c + c + "S";
+        concat = "" + z + b + s + f + b + i + l + S;
+        concat = "" + s + b + "S" + S + b + d + d + i;
+        concat = "" + "S" + c + i + z + f + l + s + f;
+        concat = "" + c + z + S + i + "S" + z + b + s;
+        concat = "" + l + i + z + z + z + s + b + d;
+        concat = "" + S + c + d + d + z + s + l + c;
+        concat = "" + l + f + i + f + S + "S" + "S" + f;
+        concat = "" + f + S + s + S + c + i + i + s;
+        concat = "" + f + l + l + z + f + s + "S" + s;
+        concat = "" + f + l + S + S + c + i + l + i;
+        concat = "" + c + s + S + c + f + c + i + i;
+        concat = "" + c + b + c + i + s + d + S + d;
+        concat = "" + z + c + b + s + "S" + z + z + c;
+        concat = "" + f + d + c + "S" + s + "S" + c + "S";
+        concat = "" + d + s + i + i + z + l + l + c;
+        concat = "" + l + z + c + c + b + z + i + d;
+        concat = "" + b + f + f + d + b + "S" + c + f;
+        concat = "" + "S" + "S" + i + z + i + z + b + l;
+        concat = "" + c + z + S + c + b + S + i + i;
+        concat = "" + z + z + z + s + i + s + S + s;
+        concat = "" + l + b + c + d + l + i + c + l;
+        concat = "" + l + l + z + d + "S" + z + b + b;
+        concat = "" + d + f + c + z + b + z + l + z;
+        concat = "" + f + S + s + d + z + S + i + s;
+        concat = "" + d + d + b + i + s + l + "S" + i;
+        concat = "" + f + "S" + b + c + "S" + s + S + d;
+        concat = "" + z + s + "S" + l + c + c + b + z;
+        concat = "" + z + b + s + i + f + d + b + i;
+        concat = "" + i + S + z + c + s + i + z + i;
+        concat = "" + c + b + c + i + i + s + f + z;
+        concat = "" + b + d + c + d + S + c + b + s;
+        concat = "" + c + S + b + z + b + i + "S" + l;
+/*
+        concat = "" + z + "S" + c + l + z + "S" + z + b;
+        concat = "" + "S" + "S" + f + c + c + b + d + c;
+        concat = "" + b + s + f + f + S + "S" + d + s;
+        concat = "" + z + "S" + b + s + l + S + s + f;
+        concat = "" + s + i + i + i + b + l + f + c;
+        concat = "" + z + s + z + z + d + S + S + c;
+        concat = "" + c + l + S + "S" + l + s + b + i;
+        concat = "" + c + s + S + f + S + b + f + s;
+        concat = "" + s + S + c + s + b + b + f + z;
+        concat = "" + c + f + f + b + z + z + b + s;
+        concat = "" + f + s + z + f + b + l + f + f;
+        concat = "" + S + d + f + c + c + s + z + S;
+        concat = "" + c + b + z + f + b + d + b + S;
+        concat = "" + b + "S" + d + b + c + d + S + f;
+        concat = "" + "S" + d + S + "S" + S + S + S + i;
+        concat = "" + f + l + l + "S" + "S" + i + c + "S";
+        concat = "" + b + i + z + "S" + l + l + d + s;
+        concat = "" + c + c + z + z + l + d + "S" + b;
+        concat = "" + S + l + s + d + s + c + d + l;
+        concat = "" + c + b + "S" + s + "S" + s + S + l;
+        concat = "" + c + c + "S" + s + z + "S" + S + c;
+        concat = "" + s + S + z + i + i + l + S + f;
+        concat = "" + s + i + i + f + f + z + "S" + c;
+        concat = "" + S + d + d + S + S + c + d + b;
+        concat = "" + b + S + S + d + c + l + "S" + "S";
+        concat = "" + S + l + i + i + b + c + f + c;
+        concat = "" + s + c + S + d + i + c + s + z;
+        concat = "" + i + f + s + b + S + z + f + "S";
+        concat = "" + "S" + s + "S" + f + "S" + "S" + l + d;
+        concat = "" + b + l + S + z + c + S + S + S;
+        concat = "" + i + d + s + i + s + S + i + f;
+        concat = "" + c + "S" + i + c + c + b + c + "S";
+        concat = "" + "S" + S + c + f + z + f + l + l;
+        concat = "" + i + S + d + d + "S" + s + i + "S";
+        concat = "" + i + l + d + d + S + l + z + S;
+        concat = "" + s + b + z + l + l + i + s + z;
+        concat = "" + f + f + i + d + d + S + "S" + s;
+        concat = "" + l + z + S + b + b + b + s + b;
+        concat = "" + d + c + c + s + "S" + i + z + "S";
+        concat = "" + "S" + b + c + b + s + f + d + i;
+        concat = "" + s + "S" + z + d + d + l + b + c;
+        concat = "" + S + S + d + i + S + z + z + c;
+        concat = "" + l + "S" + i + s + z + s + i + s;
+        concat = "" + c + z + c + z + l + d + S + l;
+        concat = "" + f + f + f + s + "S" + i + d + d;
+        concat = "" + d + f + b + i + f + f + s + f;
+        concat = "" + f + f + s + b + i + c + z + "S";
+        concat = "" + d + c + d + i + i + S + d + S;
+        concat = "" + "S" + f + f + d + c + c + f + d;
+        concat = "" + z + l + b + f + i + d + "S" + d;
+        concat = "" + b + s + f + z + c + i + l + d;
+        concat = "" + l + l + l + "S" + i + l + s + l;
+        concat = "" + "S" + z + b + S + l + z + "S" + c;
+        concat = "" + S + S + b + "S" + f + "S" + c + z;
+        concat = "" + d + c + c + d + d + f + c + b;
+        concat = "" + S + c + b + f + l + s + b + c;
+        concat = "" + s + b + l + z + i + f + b + c;
+        concat = "" + S + c + "S" + d + d + f + b + z;
+        concat = "" + l + "S" + l + z + "S" + s + S + s;
+        concat = "" + f + l + S + s + f + S + l + c;
+        concat = "" + l + d + z + z + "S" + z + i + b;
+        concat = "" + b + s + c + "S" + z + "S" + c + z;
+        concat = "" + s + l + "S" + b + s + d + b + i;
+        concat = "" + S + "S" + z + z + z + s + i + l;
+        concat = "" + "S" + "S" + z + d + c + z + c + S;
+        concat = "" + c + S + f + "S" + l + z + s + "S";
+        concat = "" + d + z + z + s + s + l + i + f;
+        concat = "" + b + b + z + b + S + l + i + S;
+        concat = "" + "S" + z + i + l + c + "S" + z + S;
+        concat = "" + c + l + d + i + d + l + S + i;
+        concat = "" + i + f + "S" + d + s + S + l + c;
+        concat = "" + c + i + d + i + l + "S" + l + d;
+        concat = "" + l + c + b + d + c + b + b + c;
+        concat = "" + i + "S" + b + s + f + d + c + l;
+        concat = "" + "S" + l + l + z + S + d + s + "S";
+        concat = "" + f + f + f + s + S + c + S + l;
+        concat = "" + "S" + f + s + S + "S" + S + "S" + s;
+        concat = "" + f + z + c + s + f + c + i + d;
+        concat = "" + c + z + d + d + c + i + l + l;
+        concat = "" + c + "S" + S + S + c + b + l + z;
+        concat = "" + f + z + S + i + S + b + l + c;
+        concat = "" + i + s + c + S + f + l + z + f;
+        concat = "" + i + S + "S" + c + z + s + i + "S";
+        concat = "" + S + "S" + d + l + "S" + l + i + "S";
+        concat = "" + l + d + f + c + S + i + b + s;
+        concat = "" + l + l + l + z + d + s + l + z;
+        concat = "" + "S" + i + d + l + f + S + c + z;
+        concat = "" + l + S + "S" + d + d + l + z + s;
+        concat = "" + c + i + b + d + "S" + "S" + s + f;
+        concat = "" + "S" + f + d + c + l + S + s + i;
+        concat = "" + d + d + f + f + l + l + f + b;
+        concat = "" + s + c + f + d + b + s + l + b;
+        concat = "" + b + f + d + s + c + z + "S" + z;
+        concat = "" + l + S + s + "S" + S + S + c + i;
+        concat = "" + f + s + i + f + b + S + i + z;
+        concat = "" + S + f + d + z + z + s + f + s;
+        concat = "" + d + "S" + s + i + b + d + c + i;
+        concat = "" + l + z + d + d + f + d + s + i;
+        concat = "" + f + s + S + i + "S" + "S" + S + S;
+        concat = "" + l + "S" + z + f + s + l + S + z;
+        concat = "" + s + "S" + b + "S" + d + z + i + d;
+        concat = "" + s + c + d + f + "S" + s + l + f;
+        concat = "" + i + s + i + i + b + l + S + f;
+        concat = "" + S + "S" + S + l + s + l + c + "S";
+        concat = "" + f + c + i + s + d + f + c + z;
+        concat = "" + f + l + c + b + z + "S" + z + z;
+        concat = "" + S + l + b + b + "S" + f + f + d;
+        concat = "" + l + S + "S" + f + b + l + z + z;
+        concat = "" + "S" + s + S + f + z + f + S + z;
+        concat = "" + S + l + z + s + d + l + z + z;
+        concat = "" + d + z + d + f + "S" + c + d + f;
+        concat = "" + S + z + d + z + f + i + "S" + l;
+        concat = "" + z + f + i + "S" + l + f + S + c;
+        concat = "" + S + d + "S" + f + b + l + s + l;
+        concat = "" + s + d + d + z + f + z + i + S;
+        concat = "" + "S" + s + f + z + b + l + z + f;
+        concat = "" + i + d + d + b + d + l + z + f;
+        concat = "" + s + S + "S" + l + S + d + c + s;
+        concat = "" + i + c + c + S + z + l + s + f;
+        concat = "" + "S" + f + "S" + z + "S" + d + l + s;
+        concat = "" + f + s + f + s + d + S + s + l;
+        concat = "" + "S" + c + b + z + d + z + z + b;
+        concat = "" + l + S + z + s + z + l + f + b;
+        concat = "" + l + S + s + s + c + z + z + S;
+        concat = "" + S + l + S + b + c + s + b + d;
+        concat = "" + l + z + b + z + "S" + l + f + S;
+        concat = "" + b + f + i + "S" + i + z + z + s;
+        concat = "" + b + S + f + c + S + S + l + f;
+        concat = "" + d + "S" + f + i + c + i + l + "S";
+        concat = "" + z + i + S + z + S + "S" + s + l;
+        concat = "" + "S" + S + b + l + "S" + "S" + s + "S";
+        concat = "" + l + d + "S" + f + s + z + b + f;
+        concat = "" + d + i + c + b + i + i + f + c;
+        concat = "" + f + i + s + "S" + l + b + "S" + S;
+        concat = "" + z + c + "S" + z + b + i + i + b;
+        concat = "" + z + S + f + S + b + s + S + S;
+        concat = "" + "S" + d + i + i + S + d + S + s;
+        concat = "" + b + S + f + S + i + s + S + f;
+        concat = "" + s + "S" + s + s + S + "S" + c + c;
+        concat = "" + c + f + c + l + i + s + l + i;
+        concat = "" + z + b + d + "S" + z + l + f + S;
+        concat = "" + b + "S" + z + s + f + c + l + c;
+        concat = "" + c + S + "S" + b + z + d + S + d;
+        concat = "" + s + l + l + f + b + s + z + d;
+        concat = "" + z + l + b + d + i + i + l + b;
+        concat = "" + f + i + s + z + "S" + l + s + f;
+        concat = "" + i + d + i + d + d + l + f + i;
+        concat = "" + S + s + s + i + d + S + z + i;
+        concat = "" + "S" + b + s + f + S + z + l + z;
+        concat = "" + l + s + b + b + "S" + c + S + i;
+        concat = "" + s + s + i + i + z + b + f + s;
+        concat = "" + S + d + i + S + s + s + l + i;
+        concat = "" + z + c + b + l + c + f + z + z;
+        concat = "" + l + s + l + c + z + s + S + z;
+        concat = "" + z + i + c + b + f + c + c + s;
+        concat = "" + S + i + c + b + S + d + c + c;
+        concat = "" + "S" + c + l + S + S + d + s + b;
+        concat = "" + "S" + b + s + f + f + "S" + S + z;
+        concat = "" + s + S + f + s + z + s + d + i;
+        concat = "" + d + s + l + c + S + z + d + z;
+        concat = "" + f + "S" + f + l + l + s + z + c;
+        concat = "" + b + i + f + s + S + c + s + s;
+        concat = "" + l + b + S + c + s + f + S + d;
+        concat = "" + d + S + s + z + "S" + "S" + "S" + z;
+        concat = "" + b + S + b + c + c + i + c + s;
+        concat = "" + b + S + S + "S" + s + c + f + f;
+        concat = "" + z + s + d + z + i + s + z + S;
+        concat = "" + f + d + c + z + d + S + d + z;
+        concat = "" + l + f + z + s + f + f + s + s;
+        concat = "" + "S" + l + S + d + b + s + s + c;
+        concat = "" + S + S + S + z + i + i + f + l;
+        concat = "" + S + s + S + "S" + "S" + b + l + c;
+        concat = "" + z + c + z + f + l + f + S + d;
+        concat = "" + s + b + c + z + S + S + s + "S";
+        concat = "" + f + z + S + d + z + i + b + z;
+        concat = "" + z + b + z + d + f + d + c + S;
+        concat = "" + S + i + "S" + i + l + c + "S" + "S";
+        concat = "" + i + s + f + c + c + z + c + c;
+        concat = "" + d + l + c + i + l + z + d + s;
+        concat = "" + b + z + b + b + "S" + z + i + z;
+        concat = "" + d + c + "S" + c + i + b + S + b;
+        concat = "" + z + i + b + "S" + i + "S" + i + "S";
+        concat = "" + d + S + i + "S" + S + d + c + f;
+        concat = "" + f + i + d + l + c + i + "S" + d;
+        concat = "" + "S" + d + S + "S" + "S" + "S" + S + S;
+        concat = "" + c + d + b + s + l + i + l + l;
+        concat = "" + s + d + b + b + f + i + z + z;
+        concat = "" + z + b + z + c + f + f + b + d;
+        concat = "" + c + b + i + z + S + l + d + d;
+        concat = "" + z + "S" + z + S + i + l + z + b;
+        concat = "" + S + "S" + "S" + b + s + i + d + c;
+        concat = "" + l + f + "S" + z + S + c + S + c;
+        concat = "" + s + d + i + s + l + "S" + l + i;
+        concat = "" + S + S + c + i + "S" + f + z + d;
+        concat = "" + c + i + S + "S" + l + l + s + c;
+        concat = "" + l + f + "S" + S + S + i + i + d;
+        concat = "" + d + b + i + b + c + z + c + f;
+        concat = "" + l + d + z + l + l + l + f + "S";
+        concat = "" + c + b + s + d + b + l + l + l;
+        concat = "" + i + z + b + d + z + S + c + i;
+        concat = "" + s + "S" + i + d + S + b + "S" + f;
+        concat = "" + z + c + "S" + S + f + f + s + f;
+        concat = "" + d + l + c + c + d + i + b + S;
+        concat = "" + b + z + b + i + l + S + c + "S";
+        concat = "" + d + f + b + d + f + c + l + l;
+        concat = "" + "S" + "S" + b + i + z + z + i + f;
+        concat = "" + l + f + b + s + f + d + l + S;
+        concat = "" + d + l + f + S + b + c + b + i;
+        concat = "" + S + l + c + s + i + "S" + l + s;
+        concat = "" + c + c + l + z + l + i + S + z;
+        concat = "" + "S" + "S" + i + d + z + l + f + b;
+        concat = "" + i + c + s + i + i + z + i + b;
+        concat = "" + f + c + b + d + "S" + f + "S" + s;
+        concat = "" + i + c + S + d + "S" + "S" + S + c;
+        concat = "" + l + s + i + c + z + b + i + l;
+        concat = "" + s + l + i + b + f + "S" + l + "S";
+        concat = "" + d + l + b + l + c + f + f + i;
+        concat = "" + d + s + d + f + f + i + i + f;
+        concat = "" + i + S + b + d + z + "S" + b + d;
+        concat = "" + z + "S" + b + l + c + S + S + "S";
+        concat = "" + s + "S" + "S" + f + d + b + l + d;
+        concat = "" + b + "S" + "S" + d + d + l + d + "S";
+        concat = "" + c + z + i + d + "S" + d + c + S;
+        concat = "" + s + l + s + d + f + b + l + c;
+        concat = "" + c + i + z + l + f + c + i + f;
+        concat = "" + b + "S" + s + c + l + s + d + c;
+        concat = "" + i + s + d + d + "S" + f + d + z;
+        concat = "" + d + z + d + c + f + l + s + S;
+        concat = "" + b + l + f + s + d + "S" + s + i;
+        concat = "" + d + "S" + c + "S" + f + l + "S" + "S";
+        concat = "" + c + c + c + "S" + d + d + "S" + z;
+        concat = "" + "S" + S + l + "S" + c + z + s + c;
+        concat = "" + "S" + s + "S" + "S" + d + s + i + "S";
+        concat = "" + s + i + S + c + l + "S" + s + c;
+        concat = "" + z + S + f + l + "S" + f + S + s;
+        concat = "" + i + c + S + z + b + "S" + d + s;
+        concat = "" + c + S + S + i + i + i + s + c;
+        concat = "" + z + s + b + f + l + "S" + b + S;
+        concat = "" + d + d + c + i + z + i + b + S;
+        concat = "" + s + i + d + z + d + l + l + S;
+        concat = "" + z + z + b + z + S + S + f + "S";
+        concat = "" + c + z + l + S + b + c + c + "S";
+        concat = "" + b + i + S + b + s + i + f + S;
+        concat = "" + z + z + "S" + s + l + i + d + c;
+        concat = "" + s + b + s + s + b + c + b + s;
+        concat = "" + b + b + f + l + d + b + c + i;
+        concat = "" + d + z + z + c + S + f + s + l;
+        concat = "" + l + f + s + c + c + i + s + b;
+        concat = "" + l + i + l + S + i + f + b + c;
+        concat = "" + b + S + "S" + s + b + S + l + S;
+        concat = "" + d + d + l + S + c + l + c + "S";
+        concat = "" + b + i + i + d + z + f + l + b;
+        concat = "" + z + f + "S" + z + b + z + z + b;
+        concat = "" + d + "S" + f + z + b + S + l + i;
+        concat = "" + b + c + d + c + l + l + d + b;
+        concat = "" + b + l + S + b + b + s + b + b;
+        concat = "" + c + c + "S" + i + S + b + f + c;
+        concat = "" + c + b + c + f + l + i + d + f;
+        concat = "" + b + l + f + S + z + l + d + z;
+        concat = "" + z + s + i + i + "S" + b + s + b;
+        concat = "" + c + i + s + S + c + "S" + s + c;
+        concat = "" + "S" + z + f + l + s + c + "S" + S;
+        concat = "" + z + "S" + S + s + "S" + b + b + l;
+        concat = "" + c + S + b + s + "S" + c + z + l;
+        concat = "" + f + c + s + l + b + c + b + i;
+        concat = "" + s + i + i + c + s + z + f + s;
+        concat = "" + d + f + i + s + s + c + s + b;
+        concat = "" + "S" + i + f + b + d + b + S + s;
+        concat = "" + l + z + "S" + c + f + d + f + l;
+        concat = "" + s + c + z + "S" + l + c + d + c;
+        concat = "" + z + d + S + s + c + "S" + s + "S";
+        concat = "" + f + s + c + s + s + c + f + c;
+        concat = "" + z + S + s + i + d + c + z + s;
+        concat = "" + l + f + l + S + z + c + c + l;
+        concat = "" + b + d + s + b + z + "S" + i + i;
+        concat = "" + s + s + i + l + d + c + c + d;
+        concat = "" + b + c + d + f + l + l + z + c;
+        concat = "" + i + i + d + z + f + c + S + l;
+        concat = "" + s + d + i + S + z + f + s + s;
+        concat = "" + f + c + "S" + "S" + z + d + "S" + z;
+        concat = "" + z + f + b + s + "S" + s + d + l;
+        concat = "" + d + i + d + l + f + i + b + S;
+        concat = "" + f + l + l + S + l + "S" + z + "S";
+        concat = "" + z + s + b + "S" + l + b + S + z;
+        concat = "" + z + z + c + d + b + l + f + b;
+        concat = "" + S + d + b + S + s + S + i + l;
+        concat = "" + f + c + f + z + f + c + d + c;
+        concat = "" + b + s + f + z + i + s + d + z;
+        concat = "" + f + z + "S" + i + "S" + l + b + "S";
+        concat = "" + "S" + i + f + d + l + f + s + f;
+        concat = "" + S + c + d + i + d + c + c + b;
+        concat = "" + l + z + c + l + f + "S" + z + s;
+        concat = "" + l + s + s + d + d + l + b + S;
+        concat = "" + b + l + c + S + s + b + l + i;
+        concat = "" + z + z + c + S + f + f + i + d;
+        concat = "" + l + i + z + c + "S" + b + f + s;
+        concat = "" + "S" + d + f + z + b + s + i + i;
+        concat = "" + b + i + d + f + b + d + c + c;
+        concat = "" + z + S + d + z + d + i + d + b;
+        concat = "" + i + b + c + z + l + c + c + f;
+        concat = "" + "S" + b + d + d + S + z + f + b;
+        concat = "" + "S" + i + d + s + c + "S" + l + b;
+        concat = "" + f + l + s + f + l + s + d + z;
+        concat = "" + z + i + f + i + d + i + z + l;
+        concat = "" + "S" + c + z + S + c + c + d + d;
+        concat = "" + l + c + i + f + "S" + f + l + c;
+        concat = "" + l + "S" + S + d + S + b + "S" + "S";
+        concat = "" + s + f + "S" + s + S + i + "S" + f;
+        concat = "" + "S" + d + "S" + b + b + S + s + b;
+        concat = "" + i + i + b + f + z + f + i + d;
+        concat = "" + S + d + c + d + d + "S" + d + S;
+        concat = "" + f + i + d + S + f + s + s + s;
+        concat = "" + l + f + i + i + i + s + "S" + z;
+        concat = "" + d + f + l + l + "S" + f + c + S;
+        concat = "" + z + i + S + i + l + z + f + i;
+        concat = "" + b + f + "S" + S + S + d + z + S;
+        concat = "" + S + l + "S" + l + f + b + f + i;
+        concat = "" + S + z + f + b + "S" + "S" + i + l;
+        concat = "" + s + d + l + S + i + s + b + l;
+        concat = "" + "S" + f + f + c + b + s + l + b;
+        concat = "" + s + i + "S" + z + S + "S" + z + f;
+        concat = "" + l + d + S + c + z + S + S + b;
+        concat = "" + f + z + d + "S" + z + z + l + c;
+        concat = "" + c + b + s + c + S + b + s + d;
+        concat = "" + l + b + S + d + c + c + d + f;
+        concat = "" + s + b + z + f + b + "S" + s + c;
+        concat = "" + c + d + s + S + l + i + "S" + S;
+        concat = "" + z + d + f + "S" + l + d + i + i;
+        concat = "" + S + b + f + "S" + s + b + "S" + c;
+        concat = "" + f + c + i + z + c + i + d + z;
+        concat = "" + b + d + f + i + z + "S" + s + b;
+        concat = "" + f + b + s + c + S + s + i + d;
+        concat = "" + s + i + d + c + z + z + f + S;
+        concat = "" + z + f + b + z + i + b + S + i;
+        concat = "" + S + c + z + z + z + S + "S" + z;
+        concat = "" + i + l + c + "S" + S + "S" + d + c;
+        concat = "" + b + z + c + z + f + s + c + b;
+        concat = "" + b + i + z + c + c + l + d + z;
+        concat = "" + d + l + S + i + i + l + d + b;
+        concat = "" + "S" + f + "S" + d + i + b + l + S;
+        concat = "" + z + s + d + s + s + z + S + f;
+        concat = "" + i + b + z + s + S + l + "S" + l;
+        concat = "" + c + l + i + f + l + b + z + s;
+        concat = "" + f + s + c + "S" + i + b + f + "S";
+        concat = "" + z + i + S + b + c + l + i + "S";
+        concat = "" + z + z + f + "S" + d + b + f + "S";
+        concat = "" + f + "S" + l + z + z + d + S + s;
+        concat = "" + "S" + b + l + d + i + l + i + b;
+        concat = "" + z + f + f + z + f + l + b + c;
+        concat = "" + d + z + z + c + "S" + "S" + S + z;
+        concat = "" + z + d + i + S + d + i + d + "S";
+        concat = "" + f + s + l + z + b + b + b + z;
+        concat = "" + S + d + d + s + d + d + l + b;
+        concat = "" + z + s + d + S + d + c + "S" + d;
+        concat = "" + s + "S" + l + f + c + i + l + z;
+        concat = "" + "S" + "S" + s + l + "S" + S + c + c;
+        concat = "" + S + S + l + b + z + b + f + z;
+        concat = "" + S + f + s + S + d + l + l + d;
+        concat = "" + d + f + i + i + l + z + "S" + d;
+        concat = "" + l + f + "S" + l + b + z + f + c;
+        concat = "" + s + S + "S" + b + "S" + "S" + l + c;
+        concat = "" + d + z + s + f + S + c + z + f;
+        concat = "" + c + z + c + s + l + S + i + l;
+        concat = "" + "S" + S + z + l + c + f + S + b;
+        concat = "" + d + S + c + b + z + f + f + c;
+        concat = "" + l + z + c + z + "S" + f + b + c;
+        concat = "" + l + S + c + l + s + "S" + i + S;
+        concat = "" + "S" + d + l + z + S + b + b + f;
+        concat = "" + c + l + f + b + z + S + d + b;
+        concat = "" + z + "S" + i + d + f + l + b + f;
+        concat = "" + d + "S" + z + z + "S" + S + z + c;
+        concat = "" + "S" + S + d + c + "S" + i + S + S;
+        concat = "" + "S" + "S" + s + f + d + s + S + f;
+        concat = "" + z + i + l + i + "S" + c + z + b;
+        concat = "" + S + S + z + z + i + b + "S" + l;
+        concat = "" + l + "S" + l + i + s + s + s + d;
+        concat = "" + l + "S" + z + b + l + f + c + f;
+        concat = "" + b + S + z + l + i + i + z + s;
+        concat = "" + z + b + d + S + i + "S" + f + l;
+        concat = "" + l + f + l + f + d + S + d + i;
+        concat = "" + b + c + b + f + z + s + S + d;
+        concat = "" + z + S + s + f + d + l + "S" + s;
+        concat = "" + s + f + c + l + z + S + i + c;
+        concat = "" + d + "S" + s + c + i + d + d + d;
+        concat = "" + z + l + d + s + i + b + c + l;
+        concat = "" + f + f + S + z + b + l + "S" + l;
+        concat = "" + z + b + b + d + b + c + f + z;
+        concat = "" + l + c + l + f + S + b + f + "S";
+        concat = "" + f + c + f + "S" + f + c + d + i;
+        concat = "" + d + S + l + s + b + d + S + z;
+        concat = "" + z + i + "S" + c + s + l + d + s;
+        concat = "" + d + "S" + i + d + i + b + z + s;
+        concat = "" + "S" + f + f + f + S + S + f + s;
+        concat = "" + "S" + c + "S" + b + S + z + i + z;
+        concat = "" + "S" + c + s + l + S + b + S + s;
+        concat = "" + i + f + S + s + z + c + i + b;
+        concat = "" + b + f + "S" + c + S + "S" + f + f;
+        concat = "" + S + z + s + f + c + d + c + S;
+        concat = "" + i + z + S + "S" + l + s + l + l;
+        concat = "" + l + l + c + f + i + b + l + c;
+        concat = "" + s + b + l + f + s + b + b + i;
+        concat = "" + c + c + z + b + i + b + f + l;
+        concat = "" + S + c + c + b + d + c + l + S;
+        concat = "" + d + z + "S" + z + i + d + S + l;
+        concat = "" + "S" + s + z + d + s + d + b + d;
+        concat = "" + S + c + "S" + s + b + d + s + c;
+        concat = "" + c + "S" + i + b + l + c + S + d;
+        concat = "" + l + z + s + b + l + b + f + b;
+        concat = "" + z + S + s + i + b + S + s + z;
+        concat = "" + z + d + f + c + S + f + f + f;
+        concat = "" + c + s + c + l + "S" + S + s + i;
+        concat = "" + s + d + S + d + "S" + l + s + f;
+        concat = "" + "S" + i + i + d + b + b + b + b;
+        concat = "" + c + l + i + "S" + c + s + c + f;
+        concat = "" + "S" + "S" + "S" + i + l + c + d + s;
+        concat = "" + d + "S" + l + s + f + z + "S" + c;
+        concat = "" + d + f + z + s + b + l + f + z;
+        concat = "" + l + l + c + i + i + d + z + z;
+        concat = "" + z + z + s + s + b + c + f + S;
+        concat = "" + b + "S" + i + z + "S" + "S" + b + d;
+        concat = "" + S + "S" + s + S + S + i + b + S;
+        concat = "" + l + d + z + d + c + c + "S" + c;
+        concat = "" + l + c + d + b + b + d + d + b;
+        concat = "" + s + z + c + i + c + "S" + "S" + "S";
+        concat = "" + s + s + "S" + b + z + c + d + S;
+        concat = "" + c + S + f + f + S + l + f + "S";
+        concat = "" + "S" + b + f + c + b + f + f + "S";
+        concat = "" + s + S + d + "S" + S + l + b + f;
+        concat = "" + b + c + l + b + f + l + b + z;
+        concat = "" + i + s + s + S + c + l + l + S;
+        concat = "" + i + b + f + z + z + S + z + l;
+        concat = "" + c + d + f + d + S + "S" + l + s;
+        concat = "" + s + S + b + c + z + z + i + s;
+        concat = "" + S + z + d + S + s + c + b + s;
+        concat = "" + b + d + c + z + l + S + z + b;
+        concat = "" + b + i + f + i + "S" + b + s + f;
+        concat = "" + b + S + z + c + z + z + b + z;
+        concat = "" + l + d + d + c + "S" + "S" + c + f;
+        concat = "" + l + s + c + l + f + f + "S" + i;
+        concat = "" + S + z + z + S + c + S + f + S;
+        concat = "" + S + c + c + b + c + S + z + f;
+        concat = "" + d + s + s + i + z + c + d + s;
+        concat = "" + s + c + b + z + f + S + z + S;
+        concat = "" + S + z + l + i + s + c + s + z;
+        concat = "" + i + l + "S" + s + b + f + l + z;
+        concat = "" + f + i + l + c + l + l + i + z;
+        concat = "" + b + "S" + l + d + l + i + c + S;
+        concat = "" + s + S + S + f + s + S + l + d;
+        concat = "" + l + l + f + d + c + s + d + c;
+        concat = "" + b + d + S + l + z + c + i + z;
+        concat = "" + z + f + "S" + z + d + z + z + f;
+        concat = "" + s + s + d + l + S + z + d + "S";
+        concat = "" + z + "S" + S + b + S + f + s + "S";
+        concat = "" + c + "S" + c + z + b + l + l + f;
+        concat = "" + S + S + S + d + i + z + s + d;
+        concat = "" + c + c + l + f + c + f + i + f;
+        concat = "" + l + c + f + i + b + f + i + f;
+        concat = "" + l + s + b + "S" + z + f + f + z;
+        concat = "" + s + z + z + i + d + s + b + z;
+        concat = "" + c + "S" + i + "S" + "S" + f + S + l;
+        concat = "" + s + c + s + d + c + S + i + s;
+        concat = "" + z + S + f + f + s + z + s + s;
+        concat = "" + "S" + S + d + l + "S" + f + s + z;
+        concat = "" + i + l + b + l + d + i + i + b;
+        concat = "" + z + d + S + c + f + S + "S" + S;
+        concat = "" + S + l + f + l + d + i + "S" + z;
+        concat = "" + d + "S" + z + z + b + z + i + l;
+        concat = "" + z + c + d + s + c + b + "S" + S;
+        concat = "" + f + S + l + f + b + f + "S" + S;
+        concat = "" + s + S + c + l + d + S + l + z;
+        concat = "" + d + i + i + i + b + l + d + d;
+        concat = "" + l + S + b + s + l + s + d + d;
+        concat = "" + b + S + l + z + f + S + c + i;
+        concat = "" + f + "S" + z + d + z + s + d + f;
+        concat = "" + b + s + d + c + c + d + z + "S";
+        concat = "" + z + "S" + i + f + b + c + "S" + l;
+        concat = "" + "S" + S + z + c + f + i + b + c;
+        concat = "" + "S" + z + d + s + f + f + "S" + s;
+        concat = "" + d + "S" + c + c + s + S + z + d;
+        concat = "" + S + s + f + "S" + f + "S" + d + i;
+        concat = "" + S + s + s + i + z + c + f + s;
+        concat = "" + c + c + i + f + z + f + c + f;
+        concat = "" + f + s + d + S + i + b + d + s;
+        concat = "" + d + z + d + "S" + l + z + f + d;
+        concat = "" + z + b + s + d + "S" + f + f + c;
+        concat = "" + S + i + l + d + z + i + f + "S";
+        concat = "" + "S" + c + i + d + z + S + i + S;
+        concat = "" + S + "S" + b + "S" + s + S + f + d;
+        concat = "" + S + z + b + "S" + i + c + i + "S";
+        concat = "" + i + l + c + d + d + f + s + S;
+        concat = "" + l + i + f + s + d + b + S + b;
+        concat = "" + b + d + S + z + b + c + d + l;
+        concat = "" + f + s + i + d + f + i + z + c;
+        concat = "" + s + c + l + c + c + l + d + z;
+        concat = "" + S + i + z + b + S + d + b + i;
+        concat = "" + f + z + l + i + z + d + c + l;
+        concat = "" + c + b + S + "S" + "S" + s + S + f;
+        concat = "" + i + "S" + f + "S" + l + z + S + b;
+        concat = "" + s + S + l + i + S + "S" + f + i;
+        concat = "" + i + c + l + "S" + l + c + c + z;
+        concat = "" + i + s + z + S + S + i + S + l;
+        concat = "" + "S" + f + S + c + f + S + d + s;
+        concat = "" + "S" + z + c + S + "S" + "S" + d + b;
+        concat = "" + s + l + z + b + z + d + i + b;
+        concat = "" + "S" + f + z + s + d + S + s + i;
+        concat = "" + f + c + f + d + S + d + S + "S";
+        concat = "" + l + z + S + "S" + S + i + z + c;
+        concat = "" + i + z + z + i + i + f + d + "S";
+        concat = "" + S + b + c + "S" + S + f + s + b;
+        concat = "" + S + c + s + s + f + b + "S" + s;
+        concat = "" + S + s + d + c + "S" + d + d + l;
+        concat = "" + i + S + "S" + l + l + l + S + d;
+        concat = "" + z + l + z + c + c + i + l + b;
+        concat = "" + s + S + b + c + z + c + "S" + s;
+        concat = "" + l + d + f + d + b + i + c + l;
+        concat = "" + i + l + "S" + c + l + "S" + c + l;
+        concat = "" + i + S + z + "S" + f + l + c + z;
+        concat = "" + c + S + l + z + b + d + s + s;
+        concat = "" + l + "S" + S + l + b + b + c + z;
+        concat = "" + b + "S" + c + c + s + f + i + z;
+        concat = "" + z + b + d + b + "S" + l + S + z;
+        concat = "" + s + f + i + S + l + S + "S" + b;
+        concat = "" + "S" + c + b + S + s + b + z + s;
+        concat = "" + l + c + z + d + z + z + c + z;
+        concat = "" + l + "S" + z + c + l + z + b + l;
+        concat = "" + d + "S" + S + c + b + d + c + d;
+        concat = "" + d + s + "S" + d + z + c + l + i;
+        concat = "" + s + l + c + f + f + "S" + d + "S";
+        concat = "" + f + s + d + z + z + d + "S" + s;
+        concat = "" + z + d + s + i + d + s + d + s;
+        concat = "" + c + z + f + i + i + i + d + f;
+        concat = "" + l + l + i + S + d + c + z + z;
+        concat = "" + "S" + l + l + b + z + z + S + c;
+        concat = "" + l + S + l + S + b + c + S + b;
+        concat = "" + b + b + b + c + b + c + f + c;
+        concat = "" + l + d + "S" + b + d + "S" + b + S;
+        concat = "" + b + c + f + S + z + l + i + "S";
+        concat = "" + l + S + s + b + l + b + b + l;
+        concat = "" + d + l + l + s + S + S + d + d;
+        concat = "" + i + s + c + S + l + "S" + i + c;
+        concat = "" + b + l + f + b + c + f + b + c;
+        concat = "" + b + l + b + z + i + b + d + i;
+        concat = "" + d + b + l + "S" + c + S + d + s;
+        concat = "" + z + l + s + l + d + d + s + z;
+        concat = "" + i + z + c + l + f + "S" + d + "S";
+        concat = "" + c + s + f + s + z + b + S + z;
+        concat = "" + "S" + d + c + l + d + z + S + f;
+        concat = "" + "S" + i + S + s + s + z + f + l;
+        concat = "" + z + d + z + S + S + l + S + f;
+        concat = "" + S + b + b + d + l + l + S + S;
+        concat = "" + S + s + z + "S" + c + i + S + s;
+        concat = "" + l + d + S + c + "S" + l + c + f;
+        concat = "" + c + d + c + s + f + c + l + i;
+        concat = "" + "S" + "S" + f + d + l + l + S + S;
+        concat = "" + z + b + z + z + "S" + l + i + s;
+        concat = "" + z + z + "S" + z + f + "S" + s + b;
+        concat = "" + s + l + f + s + b + b + c + d;
+        concat = "" + z + "S" + f + S + s + s + S + b;
+        concat = "" + z + i + l + S + s + b + b + "S";
+        concat = "" + c + c + s + z + b + i + l + z;
+        concat = "" + s + i + s + c + l + d + f + b;
+        concat = "" + i + f + s + c + i + s + S + s;
+        concat = "" + z + c + "S" + s + S + f + f + b;
+        concat = "" + l + f + z + "S" + s + S + i + i;
+        concat = "" + "S" + s + d + S + s + s + b + b;
+        concat = "" + "S" + S + "S" + d + "S" + d + z + i;
+        concat = "" + f + "S" + d + i + s + d + s + d;
+        concat = "" + f + b + S + "S" + s + b + z + "S";
+        concat = "" + S + s + S + "S" + "S" + S + d + i;
+        concat = "" + "S" + i + i + b + d + l + S + S;
+        concat = "" + S + d + f + b + d + S + b + s;
+        concat = "" + b + s + c + z + "S" + d + i + f;
+        concat = "" + S + f + z + "S" + c + d + "S" + b;
+        concat = "" + z + l + s + z + d + i + c + S;
+        concat = "" + d + z + S + "S" + b + d + z + z;
+        concat = "" + l + "S" + f + "S" + d + c + z + b;
+        concat = "" + l + b + z + f + S + s + s + "S";
+        concat = "" + i + S + b + s + d + "S" + S + z;
+        concat = "" + c + S + f + z + b + s + S + "S";
+        concat = "" + i + z + "S" + d + s + "S" + z + i;
+        concat = "" + z + s + d + s + S + z + s + "S";
+        concat = "" + "S" + s + S + d + S + S + l + S;
+        concat = "" + s + i + s + s + f + S + s + d;
+        concat = "" + c + z + s + S + f + l + S + f;
+        concat = "" + b + "S" + d + b + "S" + f + d + z;
+        concat = "" + i + f + "S" + l + "S" + i + b + c;
+        concat = "" + z + i + s + z + d + b + l + l;
+        concat = "" + l + b + b + b + S + d + b + d;
+        concat = "" + z + i + S + "S" + b + i + c + s;
+        concat = "" + S + d + f + S + i + b + "S" + f;
+        concat = "" + z + b + c + i + d + b + "S" + S;
+        concat = "" + i + S + b + S + S + d + S + s;
+        concat = "" + i + s + "S" + "S" + c + f + "S" + z;
+        concat = "" + s + l + f + l + "S" + "S" + d + d;
+        concat = "" + l + s + "S" + f + d + b + l + i;
+        concat = "" + l + f + l + i + c + s + i + b;
+        concat = "" + b + S + s + s + i + d + b + "S";
+        concat = "" + S + s + b + l + "S" + l + s + l;
+        concat = "" + b + f + b + l + "S" + l + c + b;
+        concat = "" + l + l + z + S + z + S + c + b;
+        concat = "" + z + f + c + f + b + s + S + "S";
+        concat = "" + f + S + S + i + l + b + s + f;
+        concat = "" + l + f + l + z + b + "S" + f + d;
+        concat = "" + s + d + c + z + d + d + S + l;
+        concat = "" + S + b + i + i + S + s + d + c;
+        concat = "" + b + c + d + "S" + d + i + "S" + c;
+        concat = "" + s + c + i + "S" + c + f + b + d;
+        concat = "" + l + s + l + S + d + z + b + d;
+        concat = "" + "S" + b + s + z + "S" + i + c + b;
+        concat = "" + f + c + f + z + c + i + S + f;
+        concat = "" + c + "S" + f + s + i + f + l + s;
+        concat = "" + b + s + b + b + f + b + f + s;
+        concat = "" + f + i + c + d + S + d + b + z;
+        concat = "" + b + d + l + b + S + b + l + b;
+        concat = "" + "S" + s + i + "S" + s + d + c + S;
+        concat = "" + f + "S" + i + l + z + c + b + i;
+        concat = "" + c + s + z + "S" + i + b + d + i;
+        concat = "" + "S" + f + s + b + c + i + i + S;
+        concat = "" + c + c + z + s + i + c + c + i;
+        concat = "" + d + l + c + i + b + z + c + s;
+        concat = "" + i + b + f + f + l + f + i + b;
+        concat = "" + z + S + s + S + d + l + d + s;
+        concat = "" + l + s + i + b + z + i + b + c;
+        concat = "" + l + z + l + b + z + "S" + d + d;
+        concat = "" + s + z + z + z + l + "S" + l + b;
+        concat = "" + "S" + c + i + l + c + c + d + d;
+        concat = "" + z + "S" + z + "S" + f + S + c + l;
+        concat = "" + f + f + z + b + f + l + d + c;
+        concat = "" + f + "S" + d + i + "S" + s + s + s;
+        concat = "" + c + s + c + z + b + S + d + i;
+        concat = "" + S + s + z + d + d + S + i + d;
+        concat = "" + l + s + c + b + d + z + b + i;
+        concat = "" + s + i + S + S + c + "S" + d + f;
+        concat = "" + S + c + S + "S" + S + "S" + z + l;
+        concat = "" + d + i + i + i + s + c + l + b;
+        concat = "" + S + s + b + S + b + c + l + d;
+        concat = "" + c + c + s + d + l + b + s + d;
+        concat = "" + "S" + b + S + d + l + z + S + z;
+        concat = "" + "S" + z + c + z + S + b + c + S;
+        concat = "" + s + "S" + c + "S" + z + l + l + s;
+        concat = "" + "S" + c + l + b + "S" + c + f + l;
+        concat = "" + "S" + c + b + f + "S" + b + i + f;
+        concat = "" + c + z + d + i + z + s + l + f;
+        concat = "" + S + z + z + S + l + S + S + b;
+        concat = "" + S + s + d + c + s + b + c + d;
+        concat = "" + l + S + i + i + s + d + S + i;
+        concat = "" + "S" + s + s + s + f + c + d + c;
+        concat = "" + i + S + c + d + c + c + S + i;
+        concat = "" + b + S + z + S + b + s + c + l;
+        concat = "" + d + z + s + s + i + c + d + c;
+        concat = "" + c + d + i + z + S + s + l + d;
+        concat = "" + b + f + s + "S" + b + S + S + S;
+        concat = "" + d + f + d + s + z + f + c + s;
+        concat = "" + c + l + "S" + z + l + i + d + l;
+        concat = "" + "S" + b + "S" + s + "S" + c + "S" + c;
+        concat = "" + b + f + c + "S" + d + d + d + f;
+        concat = "" + S + f + z + z + z + b + b + c;
+        concat = "" + f + s + i + s + S + z + l + s;
+        concat = "" + b + b + d + S + z + s + l + c;
+        concat = "" + d + i + d + b + i + "S" + c + "S";
+        concat = "" + c + s + "S" + i + z + s + S + i;
+        concat = "" + b + f + f + b + S + c + f + s;
+        concat = "" + b + c + i + l + S + S + l + f;
+        concat = "" + f + c + f + c + "S" + b + i + l;
+        concat = "" + f + l + i + l + l + l + i + d;
+        concat = "" + "S" + s + i + s + i + b + f + f;
+        concat = "" + f + "S" + z + "S" + "S" + c + s + s;
+        concat = "" + f + i + l + b + "S" + S + s + s;
+        concat = "" + s + f + f + f + l + c + S + b;
+        concat = "" + S + d + i + i + "S" + z + d + f;
+        concat = "" + z + c + S + c + S + c + i + z;
+        concat = "" + d + S + i + s + f + "S" + l + s;
+        concat = "" + c + b + f + z + s + l + z + c;
+        concat = "" + b + c + c + "S" + S + S + d + b;
+        concat = "" + s + f + "S" + "S" + z + l + d + d;
+        concat = "" + z + i + i + z + s + c + c + f;
+        concat = "" + s + d + d + "S" + "S" + f + f + "S";
+        concat = "" + i + S + i + i + l + l + "S" + z;
+        concat = "" + l + d + b + d + S + S + l + f;
+        concat = "" + f + i + "S" + b + i + i + c + d;
+        concat = "" + z + z + i + i + c + d + l + "S";
+        concat = "" + s + d + b + l + f + d + z + f;
+        concat = "" + d + c + "S" + d + "S" + l + d + i;
+        concat = "" + l + s + c + "S" + l + l + i + f;
+        concat = "" + s + s + i + "S" + i + s + b + i;
+        concat = "" + "S" + c + s + d + i + s + S + d;
+        concat = "" + b + l + S + b + b + i + d + b;
+        concat = "" + c + b + S + b + "S" + b + b + c;
+        concat = "" + d + "S" + S + s + c + i + z + "S";
+        concat = "" + d + S + z + c + c + c + z + f;
+        concat = "" + s + z + d + f + f + i + f + f;
+        concat = "" + d + c + l + S + s + S + i + "S";
+        concat = "" + S + f + s + s + l + b + s + z;
+        concat = "" + c + l + f + S + i + b + i + i;
+        concat = "" + l + b + b + s + c + z + f + f;
+        concat = "" + f + c + s + S + z + "S" + d + b;
+        concat = "" + i + S + l + s + z + s + s + l;
+        concat = "" + S + z + f + s + c + b + c + s;
+        concat = "" + c + b + z + s + c + l + s + i;
+        concat = "" + f + l + s + s + S + s + i + S;
+        concat = "" + s + s + b + i + d + l + d + b;
+        concat = "" + S + "S" + s + s + s + d + d + b;
+        concat = "" + b + "S" + "S" + b + S + "S" + c + b;
+        concat = "" + d + S + c + f + "S" + "S" + z + s;
+        concat = "" + i + d + i + l + c + l + d + S;
+        concat = "" + l + "S" + S + d + S + b + c + f;
+        concat = "" + l + f + c + d + d + s + d + c;
+        concat = "" + l + l + z + l + f + l + "S" + S;
+        concat = "" + s + "S" + d + f + s + l + c + f;
+        concat = "" + "S" + d + s + i + s + "S" + s + c;
+        concat = "" + "S" + S + "S" + z + d + f + S + d;
+        concat = "" + d + "S" + i + f + z + c + l + "S";
+        concat = "" + z + b + z + "S" + S + f + "S" + s;
+        concat = "" + d + z + c + c + "S" + "S" + s + c;
+        concat = "" + i + i + d + f + c + z + c + "S";
+        concat = "" + b + S + S + f + s + b + d + b;
+        concat = "" + f + i + d + "S" + l + s + c + f;
+        concat = "" + i + c + f + s + z + S + f + z;
+        concat = "" + z + b + c + S + b + "S" + s + s;
+        concat = "" + "S" + s + d + i + i + "S" + S + l;
+        concat = "" + b + b + b + c + "S" + S + i + f;
+        concat = "" + b + b + b + d + S + z + z + f;
+        concat = "" + c + z + d + s + z + l + b + b;
+        concat = "" + b + d + z + s + b + l + l + s;
+        concat = "" + "S" + s + z + b + z + S + b + s;
+        concat = "" + z + l + d + d + f + f + s + z;
+        concat = "" + f + b + b + z + s + i + z + l;
+        concat = "" + f + i + "S" + l + S + d + c + i;
+        concat = "" + "S" + i + b + c + d + S + b + "S";
+        concat = "" + s + z + l + S + S + f + S + d;
+        concat = "" + "S" + s + d + i + c + "S" + S + z;
+        concat = "" + d + l + S + s + z + c + i + c;
+        concat = "" + i + i + f + l + i + b + S + d;
+        concat = "" + d + s + b + l + z + c + "S" + z;
+        concat = "" + f + s + c + i + c + z + f + l;
+        concat = "" + S + l + i + s + f + i + S + f;
+        concat = "" + s + f + d + s + c + b + f + l;
+        concat = "" + f + l + b + "S" + S + "S" + c + "S";
+        concat = "" + l + i + d + l + z + "S" + b + b;
+        concat = "" + "S" + i + s + s + s + b + z + c;
+        concat = "" + l + i + b + s + l + "S" + c + c;
+        concat = "" + d + s + f + "S" + f + i + S + b;
+        concat = "" + S + "S" + d + b + b + d + z + z;
+        concat = "" + f + s + i + l + f + d + b + s;
+        concat = "" + c + b + s + z + "S" + i + s + f;
+        concat = "" + f + i + z + S + s + S + s + l;
+        concat = "" + l + z + "S" + l + c + l + l + "S";
+        concat = "" + f + c + z + S + c + z + s + i;
+        concat = "" + f + c + l + s + l + s + f + b;
+        concat = "" + d + l + f + "S" + "S" + d + i + i;
+        concat = "" + z + c + b + s + l + "S" + c + s;
+        concat = "" + l + b + d + s + S + z + s + i;
+        concat = "" + f + f + z + b + i + S + d + l;
+        concat = "" + l + S + c + d + z + s + S + d;
+        concat = "" + d + S + S + S + l + d + "S" + s;
+        concat = "" + d + c + d + f + f + l + S + s;
+        concat = "" + d + f + S + b + f + i + z + "S";
+        concat = "" + i + c + z + S + z + s + f + s;
+        concat = "" + d + l + c + "S" + "S" + c + b + "S";
+        concat = "" + f + f + c + c + s + l + c + S;
+        concat = "" + c + f + f + z + s + z + b + z;
+        concat = "" + d + S + b + f + c + d + S + d;
+        concat = "" + z + z + S + d + S + "S" + c + l;
+        concat = "" + z + c + S + b + c + i + c + "S";
+        concat = "" + l + s + s + z + f + f + d + c;
+        concat = "" + c + "S" + S + b + b + i + l + f;
+        concat = "" + b + "S" + c + s + b + "S" + l + s;
+        concat = "" + f + l + c + f + l + z + "S" + S;
+        concat = "" + S + S + l + s + z + b + c + i;
+        concat = "" + d + l + f + f + S + "S" + S + d;
+        concat = "" + l + c + S + i + b + s + f + c;
+        concat = "" + d + f + f + b + f + s + f + S;
+        concat = "" + d + l + l + b + d + f + "S" + d;
+        concat = "" + f + c + b + s + z + i + l + l;
+        concat = "" + "S" + z + l + c + z + f + i + "S";
+        concat = "" + i + z + f + i + S + c + "S" + i;
+        concat = "" + z + f + "S" + S + s + f + d + d;
+        concat = "" + b + d + s + i + z + s + s + c;
+        concat = "" + "S" + d + s + c + "S" + l + i + c;
+        concat = "" + s + d + b + c + b + s + d + z;
+        concat = "" + c + d + s + l + b + b + S + S;
+        concat = "" + "S" + z + s + l + f + s + c + i;
+        concat = "" + l + l + S + z + d + l + S + S;
+        concat = "" + b + f + l + b + s + z + c + f;
+        concat = "" + "S" + b + s + s + f + z + d + f;
+        concat = "" + f + s + z + S + s + "S" + z + c;
+        concat = "" + "S" + s + "S" + s + b + S + b + b;
+        concat = "" + S + S + S + f + c + f + s + s;
+        concat = "" + f + i + i + l + c + i + s + z;
+        concat = "" + c + i + S + b + s + b + b + z;
+        concat = "" + s + S + c + b + b + l + S + f;
+        concat = "" + "S" + s + d + i + i + i + "S" + s;
+        concat = "" + S + l + d + i + i + i + s + "S";
+        concat = "" + b + z + S + "S" + i + l + c + i;
+        concat = "" + c + z + S + f + f + c + z + z;
+        concat = "" + i + "S" + S + "S" + l + b + "S" + l;
+        concat = "" + i + s + z + l + "S" + S + "S" + z;
+        concat = "" + l + d + s + l + z + c + c + c;
+        concat = "" + c + l + l + "S" + b + c + l + b;
+        concat = "" + i + f + b + l + s + z + i + "S";
+        concat = "" + c + s + S + c + i + f + S + s;
+        concat = "" + "S" + i + d + z + S + i + s + b;
+        concat = "" + i + "S" + i + f + b + l + l + i;
+        concat = "" + "S" + "S" + s + l + f + d + z + c;
+        concat = "" + d + i + s + i + z + d + S + s;
+        concat = "" + S + f + z + l + c + S + l + s;
+        concat = "" + "S" + "S" + i + l + d + l + "S" + c;
+        concat = "" + f + z + f + z + S + s + d + i;
+        concat = "" + b + f + f + b + z + b + d + d;
+        concat = "" + S + z + s + S + b + S + S + "S";
+        concat = "" + S + d + i + i + s + i + l + b;
+        concat = "" + c + d + "S" + d + S + b + d + l;
+        concat = "" + b + f + d + l + i + d + l + z;
+        concat = "" + l + c + l + b + c + z + c + l;
+        concat = "" + s + z + S + S + b + S + c + b;
+        concat = "" + c + c + d + f + d + "S" + d + i;
+        concat = "" + z + s + d + i + b + z + d + d;
+        concat = "" + "S" + i + "S" + f + d + S + "S" + f;
+        concat = "" + i + z + s + S + c + d + d + f;
+        concat = "" + l + s + c + b + s + "S" + "S" + "S";
+        concat = "" + i + d + f + l + s + S + f + b;
+        concat = "" + i + d + "S" + f + s + d + s + f;
+        concat = "" + z + f + i + "S" + l + S + "S" + f;
+        concat = "" + l + d + S + s + s + s + s + d;
+        concat = "" + z + b + b + i + i + c + z + z;
+        concat = "" + c + l + c + i + S + c + s + c;
+        concat = "" + S + "S" + s + s + c + b + d + i;
+        concat = "" + i + b + d + z + l + s + l + i;
+        concat = "" + i + b + b + b + i + b + b + d;
+        concat = "" + f + i + d + l + l + b + s + z;
+        concat = "" + S + f + S + "S" + c + s + f + "S";
+        concat = "" + c + "S" + d + "S" + c + S + c + "S";
+        concat = "" + z + "S" + f + b + i + l + c + i;
+        concat = "" + l + c + b + l + f + s + S + l;
+        concat = "" + c + S + S + d + z + S + "S" + z;
+        concat = "" + d + i + d + "S" + "S" + s + s + S;
+        concat = "" + z + s + S + f + d + z + b + l;
+        concat = "" + d + z + l + "S" + d + i + s + s;
+        concat = "" + i + s + l + l + z + c + S + S;
+        concat = "" + i + S + c + S + d + S + l + s;
+        concat = "" + i + s + f + f + z + s + c + z;
+        concat = "" + f + z + S + i + s + f + "S" + b;
+        concat = "" + S + d + f + s + S + i + z + i;
+        concat = "" + f + i + d + b + s + d + "S" + l;
+        concat = "" + b + s + c + S + S + d + "S" + "S";
+        concat = "" + z + "S" + S + c + s + s + S + l;
+        concat = "" + b + S + i + s + f + s + S + z;
+        concat = "" + l + f + "S" + c + s + b + z + f;
+        concat = "" + i + i + b + i + d + "S" + i + i;
+        concat = "" + d + d + l + "S" + d + i + "S" + l;
+        concat = "" + c + i + l + S + "S" + "S" + f + "S";
+        concat = "" + s + "S" + c + i + b + S + S + b;
+        concat = "" + l + d + d + z + c + z + b + s;
+        concat = "" + d + i + S + c + d + i + c + c;
+        concat = "" + s + S + i + "S" + b + S + c + "S";
+        concat = "" + S + d + "S" + S + d + f + s + c;
+        concat = "" + s + s + l + b + s + c + c + i;
+        concat = "" + b + d + c + d + z + z + z + d;
+        concat = "" + c + c + "S" + z + l + s + c + f;
+        concat = "" + d + b + c + s + b + S + d + f;
+        concat = "" + s + b + s + f + d + "S" + f + d;
+        concat = "" + S + "S" + f + z + S + c + s + s;
+        concat = "" + S + b + d + c + l + l + s + l;
+        concat = "" + l + S + i + l + d + f + f + "S";
+        concat = "" + s + s + c + i + i + l + f + z;
+        concat = "" + i + l + "S" + s + S + s + i + S;
+        concat = "" + i + d + s + "S" + b + f + "S" + c;
+        concat = "" + c + "S" + z + b + l + s + S + i;
+        concat = "" + i + z + "S" + s + b + c + s + i;
+        concat = "" + l + f + b + f + i + d + "S" + l;
+        concat = "" + c + c + b + s + "S" + S + d + d;
+        concat = "" + S + "S" + i + f + d + s + l + S;
+        concat = "" + b + d + "S" + S + c + b + "S" + z;
+        concat = "" + c + d + i + S + f + b + b + i;
+        concat = "" + f + i + i + l + z + "S" + z + f;
+        concat = "" + z + f + "S" + c + z + l + c + f;
+        concat = "" + c + c + d + s + d + b + l + d;
+        concat = "" + "S" + s + l + l + "S" + d + l + l;
+        concat = "" + b + i + c + c + b + S + d + S;
+        concat = "" + f + d + i + s + c + b + l + b;
+        concat = "" + s + b + S + f + S + b + l + i;
+        concat = "" + z + "S" + s + i + S + i + b + c;
+        concat = "" + s + S + d + S + z + b + b + d;
+        concat = "" + i + s + "S" + z + s + S + "S" + z;
+        concat = "" + l + z + S + S + b + z + d + "S";
+        concat = "" + S + b + z + S + s + l + "S" + b;
+        concat = "" + d + b + d + z + s + i + z + f;
+        concat = "" + "S" + "S" + b + b + c + z + S + f;
+        concat = "" + d + s + i + i + b + z + "S" + s;
+        concat = "" + i + f + "S" + z + i + s + s + z;
+        concat = "" + S + f + c + z + "S" + l + "S" + "S";
+        concat = "" + "S" + f + z + S + c + d + c + s;
+        concat = "" + d + l + c + c + b + z + d + d;
+        concat = "" + b + s + d + d + b + d + b + d;
+        concat = "" + b + b + d + i + "S" + d + "S" + d;
+        concat = "" + s + c + d + "S" + z + f + z + s;
+        concat = "" + l + "S" + c + s + s + i + b + "S";
+        concat = "" + b + l + d + z + S + c + c + c;
+        concat = "" + S + b + c + b + z + z + c + f;
+        concat = "" + s + b + z + "S" + "S" + i + i + c;
+        concat = "" + z + i + f + "S" + i + c + "S" + z;
+        concat = "" + d + b + i + S + z + f + l + f;
+        concat = "" + c + f + s + "S" + s + s + b + "S";
+        concat = "" + i + c + S + d + i + "S" + d + S;
+        concat = "" + d + b + s + z + S + S + "S" + "S";
+        concat = "" + "S" + s + d + f + s + i + i + "S";
+        concat = "" + f + z + c + z + b + b + s + "S";
+        concat = "" + c + s + d + l + c + l + c + i;
+        concat = "" + f + z + S + i + z + "S" + l + S;
+        concat = "" + S + c + c + s + c + s + d + c;
+        concat = "" + l + i + l + S + l + S + "S" + l;
+        concat = "" + c + f + f + z + d + s + S + s;
+        concat = "" + f + S + d + b + i + d + f + d;
+        concat = "" + l + i + l + d + i + b + l + d;
+        concat = "" + b + "S" + d + "S" + s + f + d + S;
+        concat = "" + i + c + i + c + i + s + d + b;
+        concat = "" + d + i + z + f + "S" + "S" + d + c;
+        concat = "" + s + c + i + d + c + f + d + f;
+        concat = "" + "S" + b + i + s + "S" + d + d + S;
+        concat = "" + s + S + b + d + f + S + S + b;
+        concat = "" + f + i + s + s + c + z + b + d;
+        concat = "" + b + i + z + c + f + d + f + d;
+        concat = "" + d + i + b + f + d + "S" + s + c;
+        concat = "" + l + z + b + b + l + z + b + b;
+        concat = "" + z + S + c + "S" + "S" + S + f + z;
+        concat = "" + d + S + "S" + s + b + c + c + c;
+        concat = "" + b + b + "S" + d + l + s + f + "S";
+        concat = "" + b + d + c + "S" + b + i + c + l;
+        concat = "" + i + b + z + S + s + i + b + z;
+        concat = "" + l + s + f + f + z + l + c + b;
+        concat = "" + s + S + l + f + i + c + i + s;
+        concat = "" + d + i + S + b + s + S + S + "S";
+        concat = "" + l + s + s + z + d + S + d + l;
+        concat = "" + S + S + s + f + b + f + f + c;
+        concat = "" + i + z + b + S + S + b + f + i;
+        concat = "" + l + z + S + b + d + S + f + i;
+        concat = "" + b + i + S + l + l + b + f + l;
+        concat = "" + i + s + i + z + i + c + c + c;
+        concat = "" + i + S + l + c + l + b + d + s;
+        concat = "" + l + s + f + "S" + s + b + S + d;
+        concat = "" + f + l + f + c + s + s + S + i;
+        concat = "" + i + f + i + "S" + S + z + b + d;
+        concat = "" + S + l + S + l + S + c + c + l;
+        concat = "" + d + d + z + l + "S" + c + d + d;
+        concat = "" + b + d + c + b + l + l + f + S;
+        concat = "" + S + z + l + i + s + "S" + S + s;
+        concat = "" + i + z + b + "S" + i + b + s + c;
+        concat = "" + z + b + c + l + S + d + i + z;
+        concat = "" + "S" + c + b + i + d + z + S + i;
+        concat = "" + l + z + s + s + s + b + "S" + c;
+        concat = "" + c + z + d + b + d + z + s + b;
+        concat = "" + l + i + f + c + b + d + "S" + i;
+        concat = "" + i + S + i + f + "S" + z + d + S;
+        concat = "" + z + c + b + b + S + "S" + c + d;
+        concat = "" + s + f + c + s + b + z + "S" + S;
+        concat = "" + l + l + b + S + "S" + "S" + f + S;
+        concat = "" + S + i + f + c + l + l + c + z;
+        concat = "" + z + i + z + b + b + d + s + S;
+        concat = "" + l + z + f + i + z + i + S + "S";
+        concat = "" + i + z + i + s + "S" + c + S + l;
+        concat = "" + i + z + S + l + S + s + "S" + f;
+        concat = "" + b + d + d + l + i + z + i + l;
+        concat = "" + S + f + c + b + c + i + d + l;
+        concat = "" + f + f + S + i + l + b + b + d;
+        concat = "" + "S" + i + "S" + c + z + f + c + "S";
+        concat = "" + i + s + l + l + d + c + f + i;
+        concat = "" + c + c + "S" + z + S + f + i + f;
+        concat = "" + c + "S" + b + "S" + l + l + z + c;
+        concat = "" + d + S + s + c + s + S + c + s;
+        concat = "" + "S" + S + l + S + c + d + z + s;
+        concat = "" + b + s + c + f + l + S + s + b;
+        concat = "" + b + "S" + "S" + f + c + "S" + c + i;
+        concat = "" + l + f + S + i + s + S + l + S;
+        concat = "" + f + S + i + d + l + l + l + i;
+        concat = "" + S + i + d + i + b + d + d + c;
+        concat = "" + l + S + "S" + b + d + z + z + i;
+        concat = "" + s + l + f + "S" + z + f + f + f;
+        concat = "" + l + l + i + f + b + s + c + f;
+        concat = "" + s + i + c + S + "S" + c + c + s;
+        concat = "" + i + b + f + S + c + b + c + c;
+        concat = "" + i + b + s + z + l + f + s + "S";
+        concat = "" + S + c + f + l + c + c + i + S;
+        concat = "" + S + s + z + f + l + s + f + d;
+        concat = "" + S + z + S + S + f + S + s + S;
+        concat = "" + l + s + s + s + i + i + "S" + l;
+        concat = "" + i + f + s + c + c + f + S + d;
+        concat = "" + S + d + d + d + S + c + S + s;
+        concat = "" + S + f + z + i + S + l + S + s;
+        concat = "" + b + S + d + S + i + b + s + z;
+        concat = "" + f + d + "S" + c + "S" + c + S + b;
+        concat = "" + c + i + z + f + d + f + "S" + c;
+        concat = "" + s + c + c + S + s + z + l + s;
+        concat = "" + i + d + f + z + S + s + d + s;
+        concat = "" + z + b + s + d + d + i + f + i;
+        concat = "" + c + i + b + l + S + d + f + f;
+        concat = "" + f + d + S + l + "S" + f + s + l;
+        concat = "" + s + l + l + s + z + s + "S" + z;
+        concat = "" + S + z + d + l + l + i + s + b;
+        concat = "" + z + "S" + z + i + S + "S" + b + S;
+        concat = "" + f + S + s + z + "S" + b + "S" + S;
+        concat = "" + i + s + b + l + b + i + d + l;
+        concat = "" + i + "S" + s + b + l + i + f + d;
+        concat = "" + c + f + S + S + z + s + "S" + c;
+        concat = "" + s + c + z + l + z + S + c + z;
+        concat = "" + d + "S" + i + b + d + S + f + c;
+        concat = "" + s + z + d + c + c + c + l + S;
+        concat = "" + z + S + z + s + l + d + l + b;
+        concat = "" + S + b + i + s + "S" + l + i + "S";
+        concat = "" + "S" + z + "S" + i + S + i + l + "S";
+        concat = "" + z + c + d + l + s + l + l + s;
+        concat = "" + b + i + s + S + z + l + f + d;
+        concat = "" + s + d + d + f + l + i + i + S;
+        concat = "" + "S" + i + d + l + b + S + d + "S";
+        concat = "" + l + s + b + z + s + l + s + c;
+        concat = "" + z + l + b + S + d + s + b + d;
+        concat = "" + "S" + S + S + f + l + i + "S" + s;
+        concat = "" + i + i + i + l + d + c + z + i;
+        concat = "" + d + z + b + b + l + b + z + i;
+        concat = "" + d + S + S + c + s + s + f + c;
+        concat = "" + b + z + f + i + d + l + l + c;
+        concat = "" + i + S + d + "S" + c + l + s + c;
+        concat = "" + l + f + d + f + S + c + i + f;
+        concat = "" + i + f + c + S + d + z + f + z;
+        concat = "" + l + z + b + S + "S" + S + c + "S";
+        concat = "" + S + z + d + c + f + z + l + b;
+        concat = "" + l + b + f + f + c + f + S + z;
+        concat = "" + z + "S" + s + d + s + z + "S" + f;
+        concat = "" + d + "S" + c + z + S + d + i + c;
+        concat = "" + "S" + z + c + S + b + f + s + i;
+        concat = "" + z + "S" + d + s + f + f + d + d;
+        concat = "" + b + d + s + i + d + d + "S" + z;
+        concat = "" + c + z + l + c + l + d + z + s;
+        concat = "" + i + f + b + b + c + l + c + c;
+        concat = "" + S + s + s + l + l + "S" + "S" + f;
+        concat = "" + d + f + f + d + b + b + d + b;
+        concat = "" + S + c + "S" + S + s + d + s + s;
+        concat = "" + z + b + b + b + l + c + l + S;
+        concat = "" + z + l + z + f + S + s + d + S;
+        concat = "" + z + i + s + d + l + z + c + S;
+        concat = "" + d + f + "S" + z + b + i + S + c;
+        concat = "" + c + c + i + d + f + d + f + f;
+        concat = "" + b + d + d + S + z + f + i + b;
+        concat = "" + b + s + l + s + s + c + l + l;
+        concat = "" + S + S + S + i + b + c + b + S;
+        concat = "" + s + i + l + S + f + z + b + b;
+        concat = "" + s + f + i + l + l + s + c + S;
+        concat = "" + s + "S" + b + z + s + S + i + i;
+        concat = "" + l + s + d + d + b + i + s + c;
+        concat = "" + S + s + f + "S" + f + c + d + l;
+        concat = "" + S + S + z + f + S + s + i + b;
+        concat = "" + s + d + l + z + c + S + d + f;
+        concat = "" + i + f + S + f + d + z + b + "S";
+        concat = "" + l + d + f + d + d + b + i + z;
+        concat = "" + s + f + S + l + "S" + d + i + S;
+        concat = "" + S + S + "S" + S + c + "S" + f + s;
+        concat = "" + z + z + c + "S" + "S" + "S" + "S" + s;
+        concat = "" + l + c + z + c + f + d + i + "S";
+        concat = "" + "S" + l + z + "S" + c + c + s + S;
+        concat = "" + d + s + d + s + l + b + S + s;
+        concat = "" + l + "S" + c + f + "S" + f + d + z;
+        concat = "" + "S" + S + c + z + d + d + b + l;
+        concat = "" + d + c + s + i + "S" + S + i + d;
+        concat = "" + d + b + b + b + z + l + s + s;
+        concat = "" + f + d + b + s + l + b + S + d;
+        concat = "" + d + c + d + z + c + i + l + b;
+        concat = "" + z + S + d + b + S + f + S + c;
+        concat = "" + z + S + c + s + S + "S" + s + "S";
+        concat = "" + z + b + d + i + S + b + l + s;
+        concat = "" + i + c + s + i + f + d + "S" + b;
+        concat = "" + s + s + b + l + S + b + z + f;
+        concat = "" + S + i + f + d + z + z + s + s;
+        concat = "" + z + z + d + l + b + c + S + f;
+        concat = "" + d + l + f + l + b + l + c + i;
+        concat = "" + "S" + f + i + c + l + c + b + z;
+        concat = "" + l + l + "S" + S + c + l + z + i;
+        concat = "" + s + f + s + z + "S" + z + c + S;
+        concat = "" + d + z + i + z + b + "S" + b + b;
+        concat = "" + "S" + "S" + c + i + b + c + f + l;
+        concat = "" + d + z + i + S + s + z + s + c;
+        concat = "" + l + i + d + c + l + s + f + b;
+        concat = "" + i + s + l + l + z + c + S + b;
+        concat = "" + S + c + l + S + d + b + "S" + b;
+        concat = "" + b + s + "S" + f + b + S + i + s;
+        concat = "" + d + z + "S" + c + s + b + z + s;
+        concat = "" + b + c + s + "S" + S + b + s + z;
+        concat = "" + b + f + z + c + b + d + S + c;
+        concat = "" + f + "S" + s + c + l + f + l + c;
+        concat = "" + b + b + z + "S" + b + l + f + s;
+        concat = "" + i + f + c + f + c + f + l + i;
+        concat = "" + z + S + "S" + c + i + d + z + f;
+        concat = "" + f + c + l + b + f + b + "S" + "S";
+        concat = "" + f + i + c + S + "S" + b + f + f;
+        concat = "" + b + z + d + z + c + c + "S" + s;
+        concat = "" + s + S + b + d + f + d + c + f;
+        concat = "" + z + f + s + i + d + f + b + z;
+        concat = "" + b + f + z + l + d + c + d + z;
+        concat = "" + i + i + b + s + f + s + S + i;
+        concat = "" + s + f + c + d + z + l + l + S;
+        concat = "" + i + "S" + S + s + l + f + z + f;
+        concat = "" + "S" + i + l + d + i + S + b + l;
+        concat = "" + S + l + f + d + z + i + b + S;
+        concat = "" + f + i + c + S + d + f + "S" + b;
+        concat = "" + s + l + z + f + s + c + d + l;
+        concat = "" + f + S + s + i + f + s + b + d;
+        concat = "" + f + c + s + i + l + f + d + d;
+        concat = "" + l + l + c + c + b + d + "S" + l;
+        concat = "" + c + i + f + f + s + i + "S" + i;
+        concat = "" + l + s + c + d + d + b + d + f;
+        concat = "" + "S" + i + f + s + f + c + "S" + "S";
+        concat = "" + i + "S" + f + f + s + c + c + z;
+        concat = "" + s + b + c + i + i + b + "S" + c;
+        concat = "" + "S" + b + f + l + s + z + z + f;
+        concat = "" + s + c + i + b + z + b + i + d;
+        concat = "" + l + S + c + l + z + s + b + c;
+        concat = "" + "S" + b + s + l + f + b + z + z;
+        concat = "" + z + c + f + S + f + l + i + i;
+        concat = "" + S + f + b + b + l + f + b + c;
+        concat = "" + b + b + "S" + S + d + i + S + s;
+        concat = "" + b + "S" + b + b + l + d + f + d;
+        concat = "" + f + b + l + l + c + b + z + b;
+        concat = "" + z + S + S + s + i + s + l + z;
+        concat = "" + S + l + c + d + z + "S" + d + c;
+        concat = "" + S + i + l + l + "S" + i + z + l;
+        concat = "" + d + l + l + i + s + s + c + i;
+        concat = "" + d + S + z + i + b + i + l + b;
+        concat = "" + c + b + d + z + l + c + z + s;
+        concat = "" + z + i + c + S + d + d + b + f;
+        concat = "" + "S" + s + l + d + i + i + s + c;
+        concat = "" + c + b + i + d + l + z + s + i;
+        concat = "" + s + b + d + s + i + f + s + c;
+        concat = "" + "S" + f + d + z + z + i + f + b;
+        concat = "" + b + c + "S" + d + d + f + f + b;
+        concat = "" + f + l + l + b + d + i + z + c;
+        concat = "" + b + z + z + z + f + i + "S" + c;
+        concat = "" + c + i + i + d + d + b + f + d;
+        concat = "" + "S" + d + c + s + z + z + c + S;
+        concat = "" + f + "S" + c + c + c + z + d + z;
+        concat = "" + s + z + f + i + b + d + b + "S";
+        concat = "" + l + i + d + "S" + z + l + s + "S";
+        concat = "" + i + z + s + z + s + z + S + l;
+        concat = "" + c + b + b + "S" + c + b + s + s;
+        concat = "" + c + l + s + d + b + "S" + f + "S";
+        concat = "" + S + c + c + d + l + "S" + b + z;
+        concat = "" + S + S + s + f + d + s + b + z;
+        concat = "" + z + "S" + l + c + "S" + c + b + l;
+        concat = "" + l + b + b + s + S + S + S + z;
+        concat = "" + b + s + d + i + l + z + c + b;
+        concat = "" + f + s + "S" + d + d + s + "S" + f;
+        concat = "" + f + f + "S" + s + z + d + l + z;
+        concat = "" + "S" + z + b + f + d + d + d + S;
+        concat = "" + i + l + z + d + f + s + s + "S";
+        concat = "" + f + c + l + S + b + b + z + z;
+        concat = "" + f + f + f + S + d + c + b + S;
+        concat = "" + "S" + c + S + "S" + b + i + z + z;
+        concat = "" + s + "S" + b + c + s + d + b + b;
+        concat = "" + S + c + "S" + S + "S" + c + "S" + S;
+        concat = "" + d + z + b + l + b + "S" + i + c;
+        concat = "" + f + "S" + l + i + l + S + d + S;
+        concat = "" + d + c + s + S + c + f + "S" + d;
+        concat = "" + z + d + s + z + i + b + d + "S";
+        concat = "" + "S" + f + c + "S" + f + s + c + "S";
+        concat = "" + i + i + c + b + d + l + i + f;
+        concat = "" + "S" + "S" + z + z + b + c + "S" + i;
+        concat = "" + l + s + i + b + f + b + l + b;
+        concat = "" + S + d + z + i + "S" + c + i + l;
+        concat = "" + l + S + d + c + s + l + i + z;
+        concat = "" + b + S + l + z + i + f + z + i;
+        concat = "" + i + S + S + d + S + d + f + b;
+        concat = "" + d + z + "S" + "S" + l + c + d + c;
+        concat = "" + i + s + S + f + l + s + z + c;
+        concat = "" + S + S + z + S + s + c + d + i;
+        concat = "" + z + d + l + f + d + i + f + S;
+        concat = "" + S + z + s + b + s + S + d + b;
+        concat = "" + b + s + b + l + S + S + c + b;
+        concat = "" + i + c + b + "S" + i + s + d + b;
+        concat = "" + f + l + i + i + S + z + z + d;
+        concat = "" + S + "S" + c + b + b + z + S + i;
+        concat = "" + S + l + l + S + z + c + c + b;
+        concat = "" + i + b + b + i + b + b + l + l;
+        concat = "" + f + c + z + c + z + s + S + i;
+        concat = "" + c + c + l + b + c + s + d + d;
+        concat = "" + l + z + "S" + d + s + c + i + d;
+        concat = "" + f + b + l + d + b + z + s + c;
+        concat = "" + i + d + l + f + c + z + "S" + i;
+        concat = "" + s + s + "S" + b + b + c + f + z;
+        concat = "" + z + d + f + d + c + f + i + S;
+        concat = "" + b + "S" + f + l + S + i + z + b;
+        concat = "" + i + z + "S" + s + S + S + c + d;
+        concat = "" + i + l + d + "S" + f + f + d + f;
+        concat = "" + i + z + s + d + l + f + i + "S";
+        concat = "" + z + S + z + s + l + S + f + "S";
+        concat = "" + "S" + i + c + f + i + l + S + f;
+        concat = "" + s + f + b + c + b + b + c + S;
+        concat = "" + S + d + "S" + s + l + i + c + d;
+        concat = "" + f + s + "S" + S + d + l + z + z;
+        concat = "" + f + S + f + "S" + S + S + d + i;
+        concat = "" + s + b + z + i + "S" + S + d + i;
+        concat = "" + s + d + c + l + z + c + d + i;
+        concat = "" + c + z + l + "S" + s + "S" + s + b;
+        concat = "" + i + z + c + s + S + s + s + "S";
+        concat = "" + c + S + S + "S" + d + f + d + f;
+        concat = "" + d + s + l + f + d + s + "S" + c;
+        concat = "" + z + f + d + "S" + c + f + "S" + S;
+        concat = "" + c + z + i + l + l + z + S + z;
+        concat = "" + "S" + s + S + d + d + z + b + d;
+        concat = "" + i + "S" + i + c + d + z + s + "S";
+        concat = "" + l + f + z + d + s + f + S + S;
+        concat = "" + l + z + d + i + i + l + b + l;
+        concat = "" + i + b + S + z + d + z + i + "S";
+        concat = "" + S + S + b + "S" + b + S + i + i;
+        concat = "" + l + "S" + c + s + l + S + S + b;
+        concat = "" + s + l + c + f + "S" + b + S + b;
+        concat = "" + s + f + b + S + s + "S" + "S" + d;
+        concat = "" + s + s + c + c + l + d + "S" + f;
+        concat = "" + b + b + "S" + S + z + s + "S" + i;
+        concat = "" + b + d + "S" + f + f + l + c + i;
+        concat = "" + i + "S" + z + d + b + S + i + f;
+        concat = "" + d + l + z + c + S + "S" + S + S;
+        concat = "" + d + c + d + S + "S" + f + f + b;
+        concat = "" + i + f + s + b + s + z + s + f;
+        concat = "" + "S" + d + c + b + S + f + S + S;
+        concat = "" + S + z + i + b + i + c + z + S;
+        concat = "" + z + i + "S" + S + S + i + z + i;
+        concat = "" + i + f + "S" + "S" + s + "S" + "S" + b;
+        concat = "" + "S" + S + c + S + i + f + b + "S";
+        concat = "" + i + d + f + z + s + z + i + s;
+        concat = "" + s + f + z + l + S + b + "S" + l;
+        concat = "" + c + "S" + b + S + z + S + l + s;
+        concat = "" + c + b + c + d + c + l + b + "S";
+        concat = "" + i + i + b + "S" + d + s + S + S;
+        concat = "" + l + b + c + c + b + f + z + s;
+        concat = "" + "S" + S + s + z + z + i + l + c;
+        concat = "" + S + f + c + i + b + "S" + b + s;
+        concat = "" + c + b + "S" + f + s + s + z + l;
+        concat = "" + l + z + c + "S" + l + c + c + c;
+        concat = "" + c + z + s + s + c + z + s + b;
+        concat = "" + i + "S" + d + s + f + c + b + s;
+        concat = "" + d + l + "S" + f + l + d + d + "S";
+        concat = "" + b + s + d + s + l + c + l + f;
+        concat = "" + S + i + c + f + i + "S" + i + s;
+        concat = "" + "S" + "S" + b + i + f + b + z + s;
+        concat = "" + S + c + "S" + f + "S" + i + s + c;
+        concat = "" + c + b + b + s + S + S + "S" + b;
+        concat = "" + s + S + i + z + s + d + i + s;
+        concat = "" + "S" + "S" + "S" + z + f + c + i + i;
+        concat = "" + z + "S" + s + l + S + i + f + f;
+        concat = "" + z + b + c + l + i + b + l + S;
+        concat = "" + s + c + f + s + l + S + d + d;
+        concat = "" + z + s + b + b + z + "S" + s + S;
+        concat = "" + "S" + l + f + l + l + b + f + i;
+        concat = "" + i + z + i + d + S + s + l + d;
+        concat = "" + f + i + b + "S" + "S" + c + b + s;
+        concat = "" + "S" + S + i + c + "S" + l + z + S;
+        concat = "" + d + l + d + b + c + l + d + s;
+        concat = "" + f + s + c + i + l + i + c + f;
+        concat = "" + l + "S" + l + d + s + "S" + i + d;
+        concat = "" + f + z + "S" + "S" + b + z + "S" + S;
+        concat = "" + l + z + l + "S" + z + c + z + c;
+        concat = "" + l + "S" + S + z + S + z + b + "S";
+        concat = "" + s + s + f + s + i + c + l + l;
+        concat = "" + b + l + S + z + l + l + c + i;
+        concat = "" + d + S + b + S + "S" + z + "S" + f;
+        concat = "" + s + S + z + s + c + S + l + s;
+        concat = "" + "S" + d + S + l + f + b + z + "S";
+        concat = "" + "S" + d + c + l + "S" + b + f + S;
+        concat = "" + "S" + S + s + z + i + S + l + f;
+        concat = "" + s + i + f + f + i + l + d + f;
+        concat = "" + "S" + s + "S" + i + s + c + "S" + d;
+        concat = "" + s + f + f + i + f + l + l + c;
+        concat = "" + i + f + "S" + d + f + d + l + z;
+        concat = "" + "S" + S + i + d + c + b + i + l;
+        concat = "" + f + l + "S" + l + s + z + b + b;
+        concat = "" + z + z + f + b + b + b + l + i;
+        concat = "" + s + "S" + S + c + "S" + "S" + b + b;
+        concat = "" + S + l + s + c + c + i + S + f;
+        concat = "" + b + z + b + f + z + S + i + b;
+        concat = "" + i + d + f + l + s + "S" + s + b;
+        concat = "" + d + d + d + z + i + f + i + i;
+        concat = "" + S + d + z + s + "S" + f + l + S;
+        concat = "" + "S" + i + s + f + S + l + s + c;
+        concat = "" + i + z + S + "S" + l + z + d + z;
+        concat = "" + b + f + b + b + S + "S" + l + s;
+        concat = "" + "S" + l + s + "S" + f + "S" + l + z;
+        concat = "" + c + s + d + z + s + "S" + z + i;
+        concat = "" + s + z + S + z + "S" + i + z + b;
+        concat = "" + c + i + S + z + d + S + i + d;
+        concat = "" + s + f + l + i + S + f + l + s;
+        concat = "" + b + i + b + d + f + f + "S" + "S";
+        concat = "" + "S" + d + l + i + s + S + l + c;
+        concat = "" + i + i + b + i + z + c + l + S;
+        concat = "" + z + S + s + d + b + s + z + s;
+        concat = "" + b + c + S + "S" + "S" + b + i + l;
+        concat = "" + z + l + "S" + l + l + f + c + s;
+        concat = "" + s + "S" + b + i + b + s + z + f;
+        concat = "" + i + b + S + s + f + "S" + c + c;
+        concat = "" + z + "S" + c + c + d + "S" + z + d;
+        concat = "" + b + s + s + S + b + i + s + s;
+        concat = "" + s + "S" + c + b + S + c + s + S;
+        concat = "" + i + d + l + s + f + "S" + S + i;
+        concat = "" + S + f + b + i + z + c + f + b;
+        concat = "" + i + S + d + d + c + b + "S" + c;
+        concat = "" + d + z + i + z + c + s + d + s;
+        concat = "" + b + z + c + i + f + i + c + "S";
+        concat = "" + f + "S" + S + d + b + f + d + b;
+        concat = "" + "S" + c + l + S + z + c + z + l;
+        concat = "" + S + c + d + b + d + s + "S" + f;
+        concat = "" + z + b + "S" + d + c + i + "S" + d;
+        concat = "" + b + "S" + c + S + z + s + i + i;
+        concat = "" + s + z + i + c + i + i + i + b;
+        concat = "" + c + z + z + d + b + s + b + s;
+        concat = "" + "S" + z + l + i + S + s + l + b;
+        concat = "" + z + b + s + f + "S" + c + f + b;
+        concat = "" + "S" + b + s + S + "S" + d + i + "S";
+        concat = "" + c + c + s + b + S + l + i + f;
+        concat = "" + "S" + i + d + d + l + "S" + "S" + b;
+        concat = "" + S + l + b + c + S + c + S + S;
+        concat = "" + l + b + "S" + l + l + z + s + i;
+        concat = "" + "S" + s + f + b + b + d + b + i;
+        concat = "" + S + b + b + b + l + c + l + b;
+        concat = "" + l + c + z + s + c + s + b + s;
+        concat = "" + l + c + S + s + "S" + b + f + d;
+        concat = "" + s + d + d + i + b + i + "S" + b;
+        concat = "" + d + l + z + l + d + l + l + b;
+        concat = "" + c + i + c + "S" + f + "S" + "S" + b;
+        concat = "" + "S" + d + z + S + z + d + i + b;
+        concat = "" + c + z + b + c + d + c + c + z;
+        concat = "" + b + s + S + d + z + f + l + z;
+        concat = "" + d + z + f + "S" + z + d + f + f;
+        concat = "" + s + z + d + c + s + s + "S" + i;
+        concat = "" + i + c + z + S + b + b + z + z;
+        concat = "" + l + "S" + f + s + z + i + d + b;
+        concat = "" + s + S + l + c + S + f + z + i;
+        concat = "" + f + l + s + "S" + l + s + d + S;
+        concat = "" + l + c + b + f + f + s + S + c;
+        concat = "" + s + i + i + S + c + s + s + s;
+        concat = "" + f + c + b + c + "S" + c + i + s;
+        concat = "" + S + S + d + f + "S" + i + i + c;
+        concat = "" + d + f + s + z + S + b + s + s;
+        concat = "" + z + f + f + l + l + i + z + z;
+        concat = "" + i + c + f + b + f + d + "S" + s;
+        concat = "" + "S" + f + z + S + z + f + l + c;
+        concat = "" + S + b + s + "S" + "S" + l + l + f;
+        concat = "" + i + l + S + c + l + c + "S" + s;
+        concat = "" + c + z + S + f + l + d + c + b;
+        concat = "" + l + b + s + s + l + S + f + d;
+        concat = "" + f + c + s + "S" + "S" + d + b + b;
+        concat = "" + "S" + c + "S" + S + f + "S" + z + b;
+        concat = "" + l + i + l + "S" + s + f + i + c;
+        concat = "" + f + s + "S" + S + f + d + l + s;
+        concat = "" + i + c + b + i + b + i + i + b;
+        concat = "" + s + s + S + d + z + d + d + l;
+        concat = "" + z + s + S + s + c + "S" + f + i;
+        concat = "" + S + l + "S" + i + f + f + d + b;
+        concat = "" + d + c + d + f + f + f + S + "S";
+        concat = "" + c + f + S + f + "S" + i + z + i;
+        concat = "" + s + b + s + b + b + i + S + z;
+        concat = "" + f + b + i + S + b + i + z + l;
+        concat = "" + c + s + b + S + s + s + z + c;
+        concat = "" + S + b + b + l + d + z + i + i;
+        concat = "" + l + S + c + d + S + b + l + i;
+        concat = "" + f + f + z + "S" + z + f + S + d;
+        concat = "" + c + l + b + "S" + s + d + "S" + c;
+        concat = "" + s + "S" + l + s + S + l + c + f;
+        concat = "" + s + S + "S" + b + i + s + i + i;
+        concat = "" + s + l + z + f + b + l + i + s;
+        concat = "" + S + c + i + b + c + z + i + "S";
+        concat = "" + z + z + d + i + l + s + "S" + S;
+        concat = "" + f + d + i + "S" + "S" + l + d + z;
+        concat = "" + c + z + s + "S" + b + c + l + c;
+        concat = "" + b + d + f + b + "S" + s + d + "S";
+        concat = "" + S + d + S + s + S + S + c + f;
+        concat = "" + z + b + S + "S" + l + z + s + z;
+        concat = "" + c + f + S + "S" + f + f + f + c;
+        concat = "" + l + "S" + d + l + l + f + b + c;
+        concat = "" + z + S + d + i + S + s + S + "S";
+        concat = "" + S + d + s + f + z + z + l + d;
+        concat = "" + z + c + c + z + s + s + z + i;
+        concat = "" + c + c + i + b + l + "S" + i + d;
+        concat = "" + i + l + "S" + b + "S" + l + z + b;
+        concat = "" + c + f + "S" + c + f + d + S + "S";
+        concat = "" + i + i + "S" + c + f + z + i + "S";
+        concat = "" + f + "S" + l + f + c + s + b + i;
+        concat = "" + f + "S" + d + b + c + "S" + i + b;
+        concat = "" + S + l + l + i + b + b + z + b;
+        concat = "" + f + "S" + l + S + c + z + c + f;
+        concat = "" + l + S + l + b + "S" + s + f + d;
+        concat = "" + c + S + z + c + s + s + s + f;
+        concat = "" + d + b + d + b + c + c + d + i;
+        concat = "" + "S" + "S" + b + f + f + c + i + b;
+        concat = "" + S + S + c + s + i + z + f + b;
+        concat = "" + l + c + c + S + "S" + b + l + "S";
+        concat = "" + f + d + S + f + b + f + "S" + f;
+        concat = "" + z + z + S + s + S + b + "S" + c;
+        concat = "" + b + s + c + f + z + s + z + S;
+        concat = "" + c + c + d + "S" + i + z + l + z;
+        concat = "" + b + d + z + S + l + S + b + S;
+        concat = "" + c + S + "S" + b + "S" + i + i + c;
+        concat = "" + "S" + i + "S" + S + "S" + i + l + f;
+        concat = "" + s + l + c + l + f + f + l + S;
+        concat = "" + l + "S" + "S" + l + c + c + d + d;
+        concat = "" + l + d + z + f + d + b + c + "S";
+        concat = "" + z + S + z + b + z + z + S + f;
+        concat = "" + d + z + c + b + i + f + z + d;
+        concat = "" + d + f + l + c + d + c + S + z;
+        concat = "" + "S" + b + d + c + l + d + d + b;
+        concat = "" + "S" + S + f + d + s + S + c + f;
+        concat = "" + f + S + b + z + i + i + s + i;
+        concat = "" + f + d + "S" + l + b + f + "S" + i;
+        concat = "" + i + i + i + b + l + d + s + f;
+        concat = "" + b + l + f + z + l + b + b + l;
+        concat = "" + i + l + c + S + S + f + b + f;
+        concat = "" + l + b + f + c + f + s + z + f;
+        concat = "" + d + S + f + i + "S" + f + f + z;
+        concat = "" + d + i + "S" + S + b + "S" + c + z;
+        concat = "" + "S" + c + "S" + l + d + b + f + s;
+        concat = "" + c + z + z + d + z + b + "S" + z;
+        concat = "" + s + s + S + z + d + f + c + f;
+        concat = "" + S + S + s + l + c + "S" + f + S;
+        concat = "" + d + l + z + d + d + l + i + f;
+        concat = "" + S + s + "S" + z + S + l + f + f;
+        concat = "" + b + s + l + "S" + "S" + c + i + f;
+        concat = "" + i + b + i + "S" + z + "S" + l + "S";
+        concat = "" + "S" + s + l + z + i + d + z + "S";
+        concat = "" + z + s + z + b + "S" + i + c + d;
+        concat = "" + b + i + i + d + S + d + l + b;
+        concat = "" + S + z + d + i + "S" + s + d + f;
+        concat = "" + l + d + z + z + S + f + c + s;
+        concat = "" + z + d + f + s + d + f + i + s;
+        concat = "" + s + "S" + c + c + z + S + "S" + i;
+        concat = "" + "S" + i + f + i + b + "S" + d + z;
+        concat = "" + i + s + d + i + d + d + b + f;
+        concat = "" + s + "S" + "S" + b + l + d + c + "S";
+        concat = "" + i + "S" + f + l + s + d + d + c;
+        concat = "" + d + "S" + s + i + i + "S" + b + S;
+        concat = "" + l + S + c + i + d + b + i + i;
+        concat = "" + s + z + s + s + c + "S" + i + f;
+        concat = "" + b + f + b + "S" + s + f + S + i;
+        concat = "" + b + b + i + f + c + "S" + s + z;
+        concat = "" + i + "S" + f + s + d + f + "S" + z;
+        concat = "" + i + z + f + S + i + l + z + z;
+        concat = "" + l + s + i + i + l + z + d + d;
+        concat = "" + s + S + d + s + f + d + l + "S";
+        concat = "" + i + z + "S" + b + "S" + S + "S" + S;
+        concat = "" + i + i + d + s + l + b + f + b;
+        concat = "" + c + b + f + i + s + "S" + l + d;
+        concat = "" + f + "S" + b + s + "S" + z + c + l;
+        concat = "" + S + f + c + f + c + b + b + z;
+        concat = "" + s + d + i + S + f + b + z + f;
+        concat = "" + "S" + b + l + d + i + b + "S" + f;
+        concat = "" + c + f + l + d + c + S + i + s;
+        concat = "" + z + s + s + "S" + i + i + c + S;
+        concat = "" + l + l + z + c + "S" + s + S + f;
+        concat = "" + c + l + l + S + z + b + f + d;
+        concat = "" + b + "S" + l + z + S + i + i + s;
+        concat = "" + d + b + l + s + S + b + S + S;
+        concat = "" + z + b + l + "S" + "S" + z + d + c;
+        concat = "" + l + S + d + "S" + c + S + l + b;
+        concat = "" + S + b + i + d + s + s + f + z;
+        concat = "" + l + "S" + b + c + z + b + "S" + d;
+        concat = "" + i + d + "S" + S + S + l + z + S;
+        concat = "" + b + S + l + "S" + S + z + S + S;
+        concat = "" + "S" + c + i + l + s + l + S + s;
+        concat = "" + s + f + c + c + S + i + z + b;
+        concat = "" + S + i + f + c + d + f + l + "S";
+        concat = "" + s + z + d + S + b + d + "S" + "S";
+        concat = "" + b + c + "S" + s + l + l + l + s;
+        concat = "" + b + c + d + i + S + b + b + S;
+        concat = "" + S + S + l + "S" + f + l + z + d;
+        concat = "" + z + S + s + b + z + d + d + c;
+        concat = "" + i + i + s + f + d + S + "S" + z;
+        concat = "" + c + s + f + s + z + i + l + "S";
+        concat = "" + z + i + "S" + s + d + l + c + i;
+        concat = "" + s + f + "S" + "S" + i + l + s + S;
+        concat = "" + "S" + f + s + d + f + s + f + z;
+        concat = "" + f + i + i + l + "S" + S + f + l;
+        concat = "" + b + S + i + z + s + f + "S" + l;
+        concat = "" + z + s + l + s + l + f + i + S;
+        concat = "" + c + i + c + f + f + z + d + s;
+        concat = "" + S + f + f + S + z + S + z + s;
+        concat = "" + c + S + S + f + "S" + z + "S" + S;
+        concat = "" + S + d + S + s + s + f + l + s;
+        concat = "" + f + S + c + l + z + d + d + b;
+        concat = "" + f + s + d + z + l + i + f + b;
+        concat = "" + b + S + d + s + c + d + b + s;
+        concat = "" + b + i + S + "S" + "S" + S + f + "S";
+        concat = "" + S + f + s + d + z + c + b + S;
+        concat = "" + b + S + d + b + S + b + c + i;
+        concat = "" + f + "S" + d + b + z + S + s + l;
+        concat = "" + s + z + s + S + S + f + b + l;
+        concat = "" + b + S + l + d + d + S + z + i;
+        concat = "" + b + s + z + i + S + s + S + "S";
+        concat = "" + s + z + i + S + i + f + c + "S";
+        concat = "" + "S" + S + f + b + c + i + c + "S";
+        concat = "" + "S" + d + b + d + S + i + c + z;
+        concat = "" + z + "S" + c + f + z + d + S + c;
+        concat = "" + f + b + c + S + z + c + f + i;
+        concat = "" + d + c + "S" + i + i + d + S + S;
+        concat = "" + d + f + c + S + i + c + s + "S";
+        concat = "" + S + f + z + f + "S" + d + f + l;
+        concat = "" + s + c + f + z + "S" + b + i + d;
+        concat = "" + "S" + d + b + S + c + b + S + z;
+        concat = "" + l + c + i + d + c + s + d + s;
+        concat = "" + s + z + i + S + s + l + s + c;
+        concat = "" + S + z + S + s + s + z + S + b;
+        concat = "" + d + "S" + s + S + z + "S" + i + s;
+        concat = "" + i + d + S + b + "S" + z + l + d;
+        concat = "" + s + "S" + z + d + "S" + c + f + s;
+        concat = "" + S + z + b + f + d + c + c + z;
+        concat = "" + d + z + S + "S" + f + d + i + "S";
+        concat = "" + b + c + f + f + l + f + b + s;
+        concat = "" + "S" + i + c + f + f + i + "S" + i;
+        concat = "" + s + S + i + c + f + i + c + d;
+        concat = "" + l + d + z + d + "S" + z + "S" + c;
+        concat = "" + i + s + c + l + S + c + c + "S";
+        concat = "" + "S" + f + S + c + "S" + s + c + c;
+        concat = "" + "S" + "S" + "S" + "S" + b + z + l + z;
+        concat = "" + z + c + d + S + S + f + f + i;
+        concat = "" + i + i + S + s + b + "S" + "S" + "S";
+        concat = "" + l + b + d + b + d + i + "S" + c;
+        concat = "" + s + z + S + s + f + z + f + c;
+        concat = "" + b + f + "S" + b + c + b + d + "S";
+        concat = "" + c + c + "S" + s + l + b + z + l;
+        concat = "" + l + b + s + z + S + c + b + c;
+        concat = "" + l + f + S + b + "S" + s + c + "S";
+        concat = "" + "S" + "S" + c + c + l + S + z + d;
+        concat = "" + s + z + S + f + i + c + l + s;
+        concat = "" + l + c + d + z + S + S + l + b;
+        concat = "" + s + f + d + S + s + f + l + b;
+        concat = "" + s + f + c + i + f + c + d + z;
+        concat = "" + d + z + s + l + d + "S" + i + S;
+        concat = "" + s + i + l + s + d + l + s + c;
+        concat = "" + l + b + z + s + d + c + S + "S";
+        concat = "" + b + c + z + z + l + l + i + d;
+        concat = "" + f + f + f + b + l + l + d + z;
+        concat = "" + s + d + z + z + f + s + f + i;
+        concat = "" + S + c + l + s + s + b + i + i;
+        concat = "" + z + f + c + l + c + b + b + b;
+        concat = "" + S + z + d + "S" + b + i + f + l;
+        concat = "" + s + l + f + S + f + z + "S" + "S";
+        concat = "" + b + c + s + S + i + s + l + f;
+        concat = "" + S + "S" + d + b + f + z + s + d;
+        concat = "" + "S" + l + f + "S" + l + f + b + S;
+        concat = "" + l + l + S + s + s + z + S + b;
+        concat = "" + c + i + d + z + l + c + c + b;
+        concat = "" + l + b + b + s + s + S + z + b;
+        concat = "" + f + l + d + i + s + c + i + z;
+        concat = "" + "S" + d + "S" + S + f + i + S + z;
+        concat = "" + b + b + c + s + f + f + f + d;
+        concat = "" + f + "S" + d + f + z + "S" + "S" + "S";
+        concat = "" + z + c + "S" + S + f + b + l + d;
+        concat = "" + "S" + d + l + S + l + d + i + l;
+        concat = "" + i + "S" + d + i + c + i + S + f;
+        concat = "" + c + c + b + S + "S" + d + c + f;
+        concat = "" + c + d + f + l + c + c + s + f;
+        concat = "" + S + i + "S" + i + c + S + S + "S";
+        concat = "" + f + "S" + "S" + b + z + s + S + d;
+        concat = "" + c + l + l + "S" + b + b + c + c;
+        concat = "" + b + i + "S" + c + s + S + b + l;
+        concat = "" + z + i + z + S + S + s + b + c;
+        concat = "" + l + l + s + i + i + z + l + "S";
+        concat = "" + d + b + z + b + b + c + s + l;
+        concat = "" + z + S + s + f + s + d + S + "S";
+        concat = "" + d + "S" + s + b + f + "S" + S + f;
+        concat = "" + b + S + z + z + z + i + f + b;
+        concat = "" + b + S + S + f + "S" + c + "S" + c;
+        concat = "" + s + l + d + "S" + S + s + "S" + z;
+        concat = "" + i + d + S + b + s + z + S + l;
+        concat = "" + b + i + "S" + l + l + c + d + f;
+        concat = "" + f + "S" + b + b + c + d + f + "S";
+        concat = "" + f + f + l + d + z + f + b + z;
+        concat = "" + b + d + b + b + s + z + z + s;
+        concat = "" + "S" + "S" + S + i + z + i + b + b;
+        concat = "" + s + c + c + d + i + z + i + b;
+        concat = "" + i + i + d + S + l + b + z + "S";
+        concat = "" + "S" + b + b + c + S + S + d + f;
+        concat = "" + s + z + f + S + d + S + s + S;
+        concat = "" + l + f + b + d + d + d + b + i;
+        concat = "" + i + l + z + S + b + d + d + f;
+        concat = "" + l + b + b + f + i + b + d + S;
+        concat = "" + d + b + "S" + l + "S" + d + c + z;
+        concat = "" + l + "S" + s + "S" + d + l + f + b;
+        concat = "" + s + s + l + f + c + "S" + c + c;
+        concat = "" + i + S + S + b + S + s + l + l;
+        concat = "" + b + z + f + f + s + S + z + c;
+        concat = "" + "S" + z + "S" + l + c + f + z + "S";
+        concat = "" + "S" + S + f + l + l + z + d + S;
+        concat = "" + d + i + s + b + l + f + "S" + S;
+        concat = "" + l + "S" + c + c + l + "S" + S + z;
+        concat = "" + s + f + i + S + l + s + S + S;
+        concat = "" + d + "S" + b + l + d + "S" + S + d;
+        concat = "" + i + i + c + c + d + S + d + c;
+        concat = "" + s + b + l + i + c + l + s + c;
+        concat = "" + l + i + f + d + z + b + f + c;
+        concat = "" + d + d + c + S + S + c + z + l;
+        concat = "" + l + d + s + d + "S" + i + "S" + s;
+        concat = "" + i + d + d + i + S + f + "S" + i;
+        concat = "" + S + f + f + b + d + s + c + c;
+        concat = "" + d + "S" + S + d + "S" + s + "S" + s;
+        concat = "" + l + z + l + d + S + S + f + c;
+        concat = "" + c + i + l + f + l + f + S + f;
+        concat = "" + l + S + z + d + z + z + i + c;
+        concat = "" + c + "S" + c + c + i + z + b + i;
+        concat = "" + i + "S" + l + "S" + l + z + b + s;
+        concat = "" + f + d + f + s + d + s + "S" + i;
+        concat = "" + s + s + i + f + i + i + i + S;
+        concat = "" + l + S + "S" + f + S + z + c + b;
+        concat = "" + s + z + f + z + "S" + i + i + i;
+        concat = "" + b + s + S + c + i + s + i + l;
+        concat = "" + l + "S" + z + "S" + c + f + i + b;
+        concat = "" + f + c + l + d + d + b + f + b;
+        concat = "" + s + i + f + c + d + s + b + l;
+        concat = "" + "S" + d + c + f + b + b + S + f;
+        concat = "" + l + i + S + "S" + s + c + f + s;
+        concat = "" + l + b + f + b + d + b + z + c;
+        concat = "" + c + i + i + z + i + S + i + "S";
+        concat = "" + "S" + "S" + b + b + s + z + d + "S";
+        concat = "" + i + s + s + b + i + c + d + c;
+        concat = "" + l + d + l + i + d + "S" + S + i;
+        concat = "" + l + s + f + z + i + z + c + d;
+        concat = "" + c + s + S + s + d + l + l + c;
+        concat = "" + c + "S" + s + b + i + s + s + S;
+        concat = "" + b + s + i + f + "S" + "S" + d + "S";
+        concat = "" + f + s + b + d + S + d + f + i;
+        concat = "" + S + c + d + z + f + s + s + c;
+        concat = "" + c + s + d + c + l + i + z + c;
+        concat = "" + f + "S" + f + f + b + "S" + c + "S";
+        concat = "" + z + "S" + s + f + i + c + c + d;
+        concat = "" + d + f + c + "S" + l + f + b + S;
+        concat = "" + z + z + d + "S" + i + c + S + b;
+        concat = "" + b + d + d + z + b + S + z + b;
+        concat = "" + S + d + d + i + i + c + f + b;
+        concat = "" + l + i + d + "S" + c + S + "S" + S;
+        concat = "" + s + z + l + b + z + s + s + i;
+        concat = "" + z + s + S + f + z + b + d + s;
+        concat = "" + z + s + S + d + S + z + i + d;
+        concat = "" + c + f + i + i + z + l + b + "S";
+        concat = "" + S + i + d + l + b + "S" + s + "S";
+        concat = "" + b + i + l + f + S + "S" + d + c;
+        concat = "" + s + l + i + d + f + b + f + S;
+        concat = "" + b + S + c + i + z + s + s + S;
+        concat = "" + s + l + c + S + d + d + s + s;
+        concat = "" + d + d + S + s + d + l + f + S;
+        concat = "" + S + c + b + i + f + b + S + c;
+        concat = "" + c + z + b + z + c + l + "S" + l;
+        concat = "" + d + d + l + b + i + b + S + c;
+        concat = "" + d + i + s + d + f + "S" + l + S;
+        concat = "" + z + l + b + s + S + s + s + d;
+        concat = "" + f + c + "S" + "S" + d + z + f + b;
+        concat = "" + b + d + i + f + s + i + b + b;
+        concat = "" + d + c + c + i + "S" + s + S + c;
+        concat = "" + f + s + z + z + s + "S" + s + "S";
+        concat = "" + b + c + c + f + S + l + S + i;
+        concat = "" + s + d + i + l + s + b + b + i;
+        concat = "" + z + "S" + z + z + b + c + s + z;
+        concat = "" + i + f + b + s + i + d + S + d;
+        concat = "" + "S" + s + i + "S" + c + b + "S" + "S";
+        concat = "" + f + b + S + c + i + b + S + f;
+        concat = "" + "S" + S + l + s + l + d + "S" + s;
+        concat = "" + f + b + i + "S" + i + z + c + "S";
+        concat = "" + c + l + d + c + b + c + i + b;
+        concat = "" + i + "S" + f + c + c + c + i + c;
+        concat = "" + b + b + s + "S" + c + l + z + b;
+        concat = "" + z + b + i + f + d + "S" + s + S;
+        concat = "" + "S" + d + d + d + z + S + d + f;
+        concat = "" + z + d + s + f + l + "S" + i + f;
+        concat = "" + "S" + i + i + d + i + S + l + f;
+        concat = "" + "S" + b + d + S + l + d + b + c;
+        concat = "" + l + "S" + z + d + b + c + S + d;
+        concat = "" + f + f + z + "S" + z + f + f + z;
+        concat = "" + s + "S" + d + S + b + b + i + b;
+        concat = "" + z + d + S + c + b + l + l + b;
+        concat = "" + f + i + i + l + i + "S" + i + b;
+        concat = "" + l + c + b + z + f + "S" + s + "S";
+        concat = "" + s + z + "S" + d + f + f + "S" + d;
+        concat = "" + c + i + z + l + S + d + i + d;
+        concat = "" + f + l + "S" + z + "S" + "S" + i + "S";
+        concat = "" + z + i + b + z + d + i + "S" + l;
+        concat = "" + "S" + f + S + S + l + c + S + s;
+        concat = "" + s + i + l + d + b + l + d + b;
+        concat = "" + c + S + d + b + s + f + S + "S";
+        concat = "" + "S" + s + S + l + b + s + f + c;
+        concat = "" + c + S + z + b + z + l + c + s;
+        concat = "" + c + z + c + b + d + b + c + z;
+        concat = "" + c + b + z + S + d + S + "S" + c;
+        concat = "" + s + d + f + "S" + S + "S" + i + d;
+        concat = "" + c + l + "S" + f + "S" + b + b + "S";
+        concat = "" + "S" + l + b + b + c + S + z + c;
+        concat = "" + "S" + l + l + s + s + S + S + s;
+        concat = "" + z + d + b + "S" + d + b + l + c;
+        concat = "" + l + b + d + c + s + f + i + "S";
+        concat = "" + d + s + i + i + l + s + d + s;
+        concat = "" + "S" + b + "S" + s + d + i + S + b;
+        concat = "" + "S" + s + S + "S" + f + S + "S" + d;
+        concat = "" + "S" + f + b + l + d + f + S + l;
+        concat = "" + z + S + l + l + b + c + z + "S";
+        concat = "" + c + b + d + i + c + i + s + s;
+        concat = "" + z + z + s + f + "S" + z + l + d;
+        concat = "" + z + c + b + c + z + c + f + z;
+        concat = "" + s + i + S + d + i + i + l + i;
+        concat = "" + l + c + z + l + z + "S" + s + l;
+        concat = "" + c + b + i + c + z + S + d + b;
+        concat = "" + b + d + b + z + b + z + c + d;
+        concat = "" + b + b + z + z + z + d + i + s;
+        concat = "" + z + d + s + s + f + f + f + b;
+        concat = "" + "S" + b + z + S + S + i + s + S;
+        concat = "" + "S" + i + b + f + S + f + l + S;
+        concat = "" + d + d + f + l + d + s + f + i;
+        concat = "" + d + s + c + b + s + d + f + "S";
+        concat = "" + d + "S" + z + z + s + c + "S" + i;
+        concat = "" + s + "S" + f + "S" + z + s + b + f;
+        concat = "" + s + i + l + "S" + z + l + S + l;
+        concat = "" + i + f + S + s + c + c + f + c;
+        concat = "" + l + b + "S" + i + S + l + l + i;
+        concat = "" + z + i + d + s + "S" + z + "S" + "S";
+        concat = "" + f + c + z + b + l + f + b + l;
+        concat = "" + i + c + S + f + S + z + S + c;
+        concat = "" + i + d + d + z + d + f + f + "S";
+        concat = "" + c + s + l + c + s + "S" + f + l;
+        concat = "" + c + i + z + f + "S" + s + d + b;
+        concat = "" + S + i + S + z + S + z + f + d;
+        concat = "" + l + "S" + s + l + "S" + b + b + s;
+        concat = "" + f + f + f + z + i + b + "S" + "S";
+        concat = "" + c + b + z + b + f + s + b + i;
+        concat = "" + S + "S" + f + c + S + i + s + "S";
+        concat = "" + z + z + z + b + l + i + l + z;
+        concat = "" + f + f + "S" + c + l + f + f + b;
+        concat = "" + "S" + "S" + S + l + S + "S" + z + i;
+        concat = "" + b + c + z + i + d + b + S + l;
+        concat = "" + l + z + i + s + s + c + "S" + l;
+        concat = "" + i + b + "S" + s + c + l + s + z;
+        concat = "" + s + s + i + S + b + z + f + c;
+        concat = "" + b + "S" + "S" + i + d + i + b + b;
+        concat = "" + i + f + d + z + d + "S" + S + l;
+        concat = "" + c + "S" + l + d + c + s + d + i;
+        concat = "" + i + "S" + s + S + z + d + d + b;
+        concat = "" + l + S + b + z + s + "S" + i + s;
+        concat = "" + l + c + b + d + s + s + z + f;
+        concat = "" + d + c + i + "S" + c + l + i + d;
+        concat = "" + b + i + i + z + b + l + s + "S";
+        concat = "" + c + "S" + S + c + z + l + l + i;
+        concat = "" + f + S + c + c + f + S + f + "S";
+        concat = "" + i + z + f + f + l + i + b + d;
+        concat = "" + z + f + z + "S" + s + S + d + l;
+        concat = "" + s + d + s + z + i + c + z + f;
+        concat = "" + d + l + z + f + b + z + f + "S";
+        concat = "" + s + b + z + S + "S" + z + S + i;
+        concat = "" + S + S + i + i + b + d + s + d;
+        concat = "" + S + c + c + b + c + z + i + "S";
+        concat = "" + f + i + c + c + s + c + s + l;
+        concat = "" + d + s + d + l + d + s + d + b;
+        concat = "" + f + z + c + s + b + l + "S" + l;
+        concat = "" + l + d + f + b + d + S + "S" + c;
+        concat = "" + i + i + b + f + c + c + s + "S";
+        concat = "" + "S" + i + c + d + s + c + c + f;
+        concat = "" + s + f + c + s + z + d + c + i;
+        concat = "" + l + c + S + d + "S" + S + f + i;
+        concat = "" + f + z + "S" + b + i + f + z + z;
+        concat = "" + l + l + b + f + "S" + S + z + c;
+        concat = "" + f + s + z + b + S + f + b + l;
+        concat = "" + s + z + z + c + d + z + b + S;
+        concat = "" + f + b + i + b + f + s + z + "S";
+        concat = "" + S + "S" + i + "S" + c + s + "S" + S;
+        concat = "" + b + "S" + d + c + i + b + "S" + c;
+        concat = "" + s + i + f + b + i + S + s + c;
+        concat = "" + z + S + z + l + b + d + l + "S";
+        concat = "" + l + c + "S" + c + i + "S" + c + s;
+        concat = "" + b + b + b + S + d + f + l + f;
+        concat = "" + b + S + d + c + s + i + S + l;
+        concat = "" + f + S + S + l + c + i + d + z;
+        concat = "" + l + b + z + d + b + l + z + f;
+        concat = "" + b + d + f + b + "S" + c + c + "S";
+        concat = "" + l + s + z + "S" + z + "S" + z + d;
+        concat = "" + l + b + S + z + b + l + s + z;
+        concat = "" + S + l + S + b + "S" + s + "S" + c;
+        concat = "" + c + i + z + z + b + f + s + "S";
+        concat = "" + f + S + z + l + f + l + b + b;
+        concat = "" + z + S + d + f + l + i + f + f;
+        concat = "" + f + S + z + s + z + c + z + b;
+        concat = "" + d + s + l + i + "S" + S + z + "S";
+        concat = "" + S + f + d + z + l + b + c + f;
+        concat = "" + d + c + "S" + l + d + s + S + c;
+        concat = "" + z + l + b + c + "S" + i + S + "S";
+        concat = "" + z + b + i + z + l + d + S + s;
+        concat = "" + f + "S" + b + s + s + i + z + l;
+        concat = "" + S + c + s + s + z + d + s + z;
+        concat = "" + f + "S" + i + c + S + f + c + S;
+        concat = "" + i + c + S + l + d + z + f + "S";
+        concat = "" + s + d + "S" + "S" + d + f + d + z;
+        concat = "" + "S" + l + S + "S" + c + s + s + d;
+        concat = "" + b + z + s + S + d + f + b + l;
+        concat = "" + c + "S" + z + d + l + i + b + f;
+        concat = "" + s + d + z + z + "S" + l + i + "S";
+        concat = "" + c + i + b + z + "S" + l + "S" + i;
+        concat = "" + f + f + s + z + i + b + i + f;
+        concat = "" + b + z + f + s + z + c + b + z;
+        concat = "" + z + z + s + s + b + z + z + s;
+        concat = "" + d + b + f + "S" + b + c + i + b;
+        concat = "" + i + d + c + i + d + d + z + f;
+        concat = "" + d + i + l + l + i + "S" + S + S;
+        concat = "" + "S" + z + f + "S" + d + b + d + i;
+        concat = "" + "S" + s + s + d + b + "S" + S + d;
+        concat = "" + i + b + s + S + d + c + c + z;
+        concat = "" + i + b + l + l + "S" + l + l + s;
+        concat = "" + i + z + s + "S" + l + c + f + i;
+        concat = "" + l + i + S + b + c + S + z + S;
+        concat = "" + "S" + l + s + z + d + d + s + i;
+        concat = "" + b + "S" + s + s + c + c + i + c;
+        concat = "" + z + i + f + z + b + c + "S" + f;
+        concat = "" + f + d + s + d + s + f + f + S;
+        concat = "" + "S" + f + f + d + S + c + l + f;
+        concat = "" + f + i + l + b + c + c + b + b;
+        concat = "" + s + l + f + i + c + i + b + S;
+        concat = "" + "S" + d + b + c + f + i + i + d;
+        concat = "" + "S" + s + s + f + z + c + b + z;
+        concat = "" + "S" + l + c + f + b + z + c + l;
+        concat = "" + s + f + d + f + c + c + b + f;
+        concat = "" + c + l + S + l + l + z + i + s;
+        concat = "" + f + d + l + i + c + z + S + l;
+        concat = "" + "S" + s + "S" + c + f + b + s + b;
+        concat = "" + c + l + S + d + b + i + c + S;
+        concat = "" + c + "S" + z + d + s + c + b + i;
+        concat = "" + z + i + b + i + i + "S" + "S" + c;
+        concat = "" + s + S + "S" + b + b + s + i + i;
+        concat = "" + S + z + c + i + "S" + f + "S" + f;
+        concat = "" + z + S + S + z + S + d + i + s;
+        concat = "" + z + f + c + l + S + l + z + i;
+        concat = "" + z + i + b + d + i + l + d + i;
+        concat = "" + z + l + l + b + z + S + l + z;
+        concat = "" + b + s + f + l + b + b + i + d;
+        concat = "" + S + S + i + z + c + z + b + s;
+        concat = "" + c + "S" + i + l + l + i + i + i;
+        concat = "" + d + b + l + S + c + b + s + z;
+        concat = "" + d + "S" + l + b + b + i + s + c;
+        concat = "" + f + z + f + b + S + z + d + S;
+        concat = "" + b + b + z + l + b + l + i + i;
+        concat = "" + "S" + b + l + f + c + i + s + "S";
+        concat = "" + s + l + d + c + l + f + d + d;
+        concat = "" + c + z + f + "S" + "S" + l + c + "S";
+        concat = "" + S + d + l + S + S + z + s + c;
+        concat = "" + S + "S" + z + S + s + c + S + z;
+        concat = "" + f + "S" + c + s + i + "S" + "S" + "S";
+        concat = "" + c + z + S + z + c + l + d + "S";
+        concat = "" + f + z + f + b + l + "S" + s + i;
+        concat = "" + l + l + i + c + f + b + z + "S";
+        concat = "" + f + b + l + b + i + c + c + S;
+        concat = "" + S + z + l + i + f + "S" + c + "S";
+        concat = "" + d + f + c + i + i + c + z + s;
+        concat = "" + S + d + s + z + z + f + b + b;
+        concat = "" + l + i + d + l + c + c + l + z;
+        concat = "" + s + l + c + b + c + "S" + z + l;
+        concat = "" + z + z + S + b + s + s + "S" + z;
+        concat = "" + c + c + f + l + i + b + b + c;
+        concat = "" + "S" + i + z + S + f + s + l + b;
+        concat = "" + s + l + b + i + c + S + S + s;
+        concat = "" + s + b + "S" + b + c + b + b + l;
+        concat = "" + c + z + z + d + "S" + "S" + d + "S";
+        concat = "" + s + S + s + s + b + i + "S" + z;
+        concat = "" + "S" + i + z + s + S + f + i + S;
+        concat = "" + S + s + S + l + z + S + d + z;
+        concat = "" + S + d + b + l + b + c + c + "S";
+        concat = "" + S + z + f + d + c + f + "S" + S;
+        concat = "" + c + c + S + i + i + s + b + S;
+        concat = "" + c + b + "S" + z + S + s + b + f;
+        concat = "" + f + "S" + d + d + c + b + f + c;
+        concat = "" + b + d + f + "S" + s + z + f + i;
+        concat = "" + b + S + "S" + S + f + z + d + z;
+        concat = "" + f + i + S + c + i + d + f + d;
+        concat = "" + l + i + z + S + z + l + z + "S";
+        concat = "" + z + "S" + d + i + z + f + i + f;
+        concat = "" + s + s + S + l + l + b + d + l;
+        concat = "" + l + i + d + z + i + c + c + z;
+        concat = "" + S + i + S + c + l + S + l + S;
+        concat = "" + d + d + d + f + s + d + i + d;
+        concat = "" + z + s + S + d + S + s + d + S;
+        concat = "" + b + l + "S" + i + z + i + d + l;
+        concat = "" + d + d + d + "S" + c + d + b + l;
+        concat = "" + i + z + i + S + s + l + s + f;
+        concat = "" + S + b + l + f + b + "S" + s + l;
+        concat = "" + "S" + s + i + f + l + f + i + S;
+        concat = "" + "S" + d + "S" + b + s + d + z + s;
+        concat = "" + i + b + s + s + b + z + S + i;
+        concat = "" + s + i + c + i + f + c + l + c;
+        concat = "" + l + S + "S" + s + c + "S" + "S" + S;
+        concat = "" + d + f + b + s + s + f + S + f;
+        concat = "" + f + s + "S" + z + f + b + z + b;
+        concat = "" + l + f + f + l + S + l + b + "S";
+        concat = "" + i + d + z + s + b + S + c + S;
+        concat = "" + c + b + d + c + "S" + i + b + l;
+        concat = "" + S + "S" + c + f + z + "S" + b + b;
+        concat = "" + S + b + S + i + "S" + b + l + S;
+        concat = "" + S + b + s + b + d + f + i + "S";
+        concat = "" + c + S + b + S + c + s + d + d;
+        concat = "" + S + "S" + l + c + c + S + "S" + s;
+        concat = "" + S + s + i + i + s + s + i + s;
+        concat = "" + b + d + b + z + l + "S" + "S" + c;
+        concat = "" + z + S + b + d + b + i + b + "S";
+        concat = "" + b + b + f + b + l + f + c + f;
+        concat = "" + l + "S" + b + i + c + b + f + l;
+        concat = "" + "S" + z + b + "S" + f + c + l + b;
+        concat = "" + c + c + S + S + "S" + f + d + l;
+        concat = "" + z + f + d + z + z + i + i + i;
+        concat = "" + s + "S" + l + l + d + S + c + f;
+        concat = "" + b + s + i + l + c + c + S + S;
+        concat = "" + b + s + i + "S" + b + b + b + d;
+        concat = "" + z + "S" + S + z + i + z + d + d;
+        concat = "" + s + S + f + "S" + b + S + "S" + z;
+        concat = "" + f + S + d + c + S + d + l + c;
+        concat = "" + i + z + f + S + "S" + b + d + c;
+        concat = "" + f + c + l + "S" + i + i + f + d;
+        concat = "" + s + i + i + i + S + i + c + d;
+        concat = "" + d + l + d + l + i + s + S + f;
+        concat = "" + f + s + c + S + b + "S" + d + f;
+        concat = "" + s + l + c + b + s + b + b + s;
+        concat = "" + b + c + l + f + i + c + s + z;
+        concat = "" + "S" + i + i + "S" + l + d + c + i;
+        concat = "" + b + l + b + i + S + f + s + l;
+        concat = "" + c + s + b + s + d + c + l + i;
+        concat = "" + f + "S" + i + c + "S" + f + s + b;
+        concat = "" + z + d + c + f + c + c + d + b;
+        concat = "" + l + b + z + z + c + c + f + S;
+        concat = "" + z + S + d + l + d + "S" + b + l;
+        concat = "" + b + "S" + b + S + l + "S" + f + z;
+        concat = "" + S + z + i + l + d + l + l + l;
+        concat = "" + f + l + d + z + b + i + "S" + "S";
+        concat = "" + S + c + l + s + b + l + b + d;
+        concat = "" + z + i + z + d + l + d + b + i;
+        concat = "" + z + s + f + c + s + b + b + f;
+        concat = "" + "S" + b + l + S + s + d + s + l;
+        concat = "" + c + i + l + "S" + f + c + c + s;
+        concat = "" + c + l + z + S + c + c + S + b;
+        concat = "" + f + d + i + f + "S" + S + b + f;
+        concat = "" + f + c + d + c + f + i + s + d;
+        concat = "" + c + z + S + i + S + c + d + f;
+        concat = "" + b + d + S + c + "S" + d + l + "S";
+        concat = "" + z + c + l + b + s + z + f + f;
+        concat = "" + b + b + s + d + l + b + z + b;
+        concat = "" + l + f + c + f + S + s + b + c;
+        concat = "" + f + c + s + f + "S" + b + c + c;
+        concat = "" + "S" + "S" + b + d + d + "S" + s + l;
+        concat = "" + c + i + d + s + b + i + s + c;
+        concat = "" + c + c + z + z + l + s + S + i;
+        concat = "" + s + b + d + z + l + b + "S" + f;
+        concat = "" + f + c + c + c + s + s + s + S;
+        concat = "" + "S" + "S" + c + S + b + b + b + i;
+        concat = "" + d + i + "S" + b + d + c + f + z;
+        concat = "" + b + S + z + s + d + S + z + z;
+        concat = "" + i + l + i + s + s + b + i + z;
+        concat = "" + s + d + c + l + b + f + S + z;
+        concat = "" + f + c + z + b + b + f + b + c;
+        concat = "" + b + d + s + b + s + s + l + "S";
+        concat = "" + "S" + d + s + s + l + s + i + d;
+        concat = "" + S + c + b + c + b + d + "S" + S;
+        concat = "" + c + "S" + c + c + c + z + s + d;
+        concat = "" + s + "S" + s + l + S + b + l + c;
+        concat = "" + i + S + S + b + z + S + s + l;
+        concat = "" + l + i + "S" + s + i + S + i + S;
+        concat = "" + S + f + f + f + b + "S" + f + z;
+        concat = "" + d + S + S + d + f + i + b + b;
+        concat = "" + z + i + i + b + l + f + z + d;
+        concat = "" + c + c + i + f + f + l + i + c;
+        concat = "" + z + f + S + c + b + S + z + s;
+        concat = "" + "S" + S + z + s + b + f + S + "S";
+        concat = "" + l + z + S + z + "S" + b + S + "S";
+        concat = "" + d + s + s + i + d + s + z + z;
+        concat = "" + f + i + f + z + S + z + z + i;
+        concat = "" + b + d + s + b + l + s + "S" + d;
+        concat = "" + l + f + f + i + c + z + z + c;
+        concat = "" + S + s + z + d + c + l + l + "S";
+        concat = "" + z + d + c + f + i + "S" + d + c;
+        concat = "" + S + "S" + z + d + c + b + l + "S";
+        concat = "" + b + c + d + d + f + i + i + b;
+        concat = "" + s + c + i + c + d + z + S + d;
+        concat = "" + f + c + "S" + d + c + S + z + "S";
+        concat = "" + "S" + b + l + "S" + i + l + b + d;
+        concat = "" + c + d + c + "S" + c + d + b + l;
+        concat = "" + l + c + b + z + s + S + d + d;
+        concat = "" + d + d + c + c + d + b + s + l;
+        concat = "" + d + c + c + S + "S" + c + s + b;
+        concat = "" + s + i + i + i + d + z + c + f;
+        concat = "" + s + S + z + d + d + c + d + f;
+        concat = "" + l + b + f + s + f + c + f + b;
+        concat = "" + d + b + z + S + b + s + d + l;
+        concat = "" + s + z + f + b + S + s + z + "S";
+        concat = "" + z + i + z + d + i + S + i + s;
+        concat = "" + b + b + f + i + c + i + i + s;
+        concat = "" + f + S + b + b + S + d + "S" + s;
+        concat = "" + i + S + i + "S" + i + S + S + f;
+        concat = "" + b + s + s + z + z + s + "S" + i;
+        concat = "" + s + b + S + "S" + l + i + s + s;
+        concat = "" + f + z + s + z + s + f + d + i;
+        concat = "" + z + l + s + c + i + l + S + l;
+        concat = "" + S + S + b + i + S + S + l + z;
+        concat = "" + z + S + l + "S" + z + z + f + "S";
+        concat = "" + l + S + f + c + f + f + s + S;
+        concat = "" + S + i + d + i + z + b + i + b;
+        concat = "" + c + l + f + d + i + f + b + d;
+        concat = "" + S + s + c + d + d + c + i + "S";
+        concat = "" + "S" + S + c + s + c + b + z + d;
+        concat = "" + l + i + c + s + "S" + d + S + b;
+        concat = "" + "S" + c + f + l + S + i + s + i;
+        concat = "" + b + s + "S" + i + d + c + b + s;
+        concat = "" + d + "S" + z + b + f + z + s + f;
+        concat = "" + s + b + b + d + i + "S" + l + S;
+        concat = "" + "S" + f + l + d + f + i + l + b;
+        concat = "" + s + i + S + S + s + b + i + l;
+        concat = "" + "S" + i + s + i + f + f + l + f;
+        concat = "" + b + c + b + i + i + i + d + i;
+        concat = "" + l + c + l + c + c + S + i + S;
+        concat = "" + "S" + S + "S" + b + s + b + i + s;
+        concat = "" + i + s + "S" + s + "S" + "S" + z + d;
+        concat = "" + d + z + c + b + c + S + z + S;
+        concat = "" + l + c + b + b + l + c + z + l;
+        concat = "" + b + "S" + i + c + l + S + c + z;
+        concat = "" + c + c + f + l + i + "S" + i + z;
+        concat = "" + z + "S" + z + z + s + i + d + c;
+        concat = "" + z + d + c + z + i + S + "S" + S;
+        concat = "" + "S" + d + "S" + b + z + f + S + d;
+        concat = "" + c + z + z + l + l + S + b + l;
+        concat = "" + d + c + i + i + i + s + b + f;
+        concat = "" + "S" + d + f + z + c + b + c + s;
+        concat = "" + b + s + "S" + d + f + i + i + s;
+        concat = "" + i + "S" + i + "S" + l + z + z + z;
+        concat = "" + i + S + i + "S" + z + i + S + z;
+        concat = "" + z + "S" + S + c + s + s + c + s;
+        concat = "" + b + f + z + c + c + l + f + d;
+        concat = "" + z + l + s + f + s + l + c + d;
+        concat = "" + c + z + s + S + c + S + c + d;
+        concat = "" + s + s + z + d + "S" + i + S + "S";
+        concat = "" + "S" + f + d + "S" + d + f + c + b;
+        concat = "" + b + z + b + s + S + "S" + b + b;
+        concat = "" + l + d + d + d + l + "S" + i + d;
+        concat = "" + d + "S" + d + b + s + c + S + f;
+        concat = "" + f + d + l + S + s + i + b + S;
+        concat = "" + z + c + b + b + f + b + i + z;
+        concat = "" + s + f + l + c + l + z + d + z;
+        concat = "" + l + S + i + d + z + z + s + d;
+        concat = "" + i + c + i + d + z + S + "S" + i;
+        concat = "" + d + c + z + "S" + z + l + z + z;
+        concat = "" + s + s + d + S + s + c + f + z;
+        concat = "" + l + "S" + l + l + "S" + z + z + S;
+        concat = "" + b + b + d + z + i + b + s + d;
+        concat = "" + d + d + d + S + i + c + "S" + z;
+        concat = "" + l + "S" + l + S + f + "S" + b + c;
+        concat = "" + s + z + "S" + S + f + S + d + b;
+        concat = "" + l + s + d + "S" + f + b + s + b;
+        concat = "" + f + S + i + z + z + l + l + f;
+        concat = "" + "S" + S + b + b + i + f + d + b;
+        concat = "" + s + b + i + c + S + S + l + d;
+        concat = "" + l + z + "S" + f + "S" + "S" + b + z;
+        concat = "" + b + z + f + S + s + d + d + c;
+        concat = "" + f + d + l + S + b + l + c + S;
+        concat = "" + c + z + b + s + z + s + S + b;
+        concat = "" + i + i + b + b + S + z + b + i;
+        concat = "" + f + s + i + s + l + d + i + z;
+        concat = "" + z + c + S + s + b + f + b + l;
+        concat = "" + s + c + "S" + "S" + b + "S" + S + f;
+        concat = "" + b + b + i + z + l + z + "S" + i;
+        concat = "" + f + s + s + z + f + f + d + "S";
+        concat = "" + z + b + "S" + s + f + S + f + b;
+        concat = "" + b + S + f + l + S + i + c + s;
+        concat = "" + i + "S" + i + S + s + l + b + s;
+        concat = "" + i + c + b + f + S + i + s + s;
+        concat = "" + z + S + s + d + b + l + l + S;
+        concat = "" + z + i + s + l + b + s + d + c;
+        concat = "" + s + s + c + f + z + S + b + S;
+        concat = "" + "S" + S + b + s + i + f + S + c;
+        concat = "" + i + "S" + l + z + d + f + b + i;
+        concat = "" + f + "S" + i + z + f + c + S + z;
+        concat = "" + "S" + z + f + i + c + S + s + z;
+        concat = "" + z + z + b + b + S + c + s + b;
+        concat = "" + c + s + d + S + i + S + S + i;
+        concat = "" + l + s + z + l + S + s + z + i;
+        concat = "" + l + z + b + z + b + b + "S" + s;
+        concat = "" + d + S + d + S + s + b + l + d;
+        concat = "" + s + z + "S" + s + l + d + "S" + S;
+        concat = "" + f + "S" + S + c + c + l + s + S;
+        concat = "" + b + i + s + d + d + d + "S" + i;
+        concat = "" + S + "S" + S + S + "S" + z + S + S;
+        concat = "" + s + l + S + b + d + i + c + s;
+        concat = "" + "S" + z + d + d + f + z + b + c;
+        concat = "" + c + b + d + s + S + c + S + S;
+        concat = "" + f + b + z + c + s + c + i + b;
+        concat = "" + f + "S" + d + f + i + "S" + z + f;
+        concat = "" + z + f + b + z + z + i + z + l;
+        concat = "" + d + s + c + s + "S" + i + s + z;
+        concat = "" + i + z + f + s + f + s + i + S;
+        concat = "" + "S" + s + z + "S" + d + "S" + c + S;
+        concat = "" + d + d + i + l + b + l + b + S;
+        concat = "" + z + i + d + S + s + s + i + z;
+        concat = "" + d + b + S + S + c + d + d + i;
+        concat = "" + c + d + z + c + l + z + z + f;
+        concat = "" + s + i + z + s + d + "S" + d + "S";
+        concat = "" + d + i + S + l + z + l + i + d;
+        concat = "" + s + l + i + i + z + S + f + f;
+        concat = "" + c + c + l + S + l + S + i + l;
+        concat = "" + "S" + f + c + c + s + b + d + l;
+        concat = "" + c + c + S + c + s + f + f + S;
+        concat = "" + s + l + i + s + f + S + s + f;
+        concat = "" + f + c + f + i + S + i + s + s;
+        concat = "" + c + f + S + f + c + "S" + S + f;
+        concat = "" + i + f + z + "S" + "S" + z + f + s;
+        concat = "" + z + d + "S" + l + i + d + z + f;
+        concat = "" + S + S + d + c + s + "S" + c + c;
+        concat = "" + l + z + d + d + b + z + c + S;
+        concat = "" + d + l + "S" + l + S + d + c + l;
+        concat = "" + b + z + S + d + f + "S" + i + c;
+        concat = "" + z + "S" + S + d + d + c + "S" + "S";
+        concat = "" + c + z + l + l + b + f + f + l;
+        concat = "" + d + s + S + c + i + s + z + d;
+        concat = "" + s + z + z + f + l + b + i + z;
+        concat = "" + i + l + f + s + d + S + b + S;
+        concat = "" + z + b + l + c + "S" + c + d + c;
+        concat = "" + z + z + c + l + S + d + i + z;
+        concat = "" + b + l + l + S + b + "S" + c + l;
+        concat = "" + "S" + s + S + b + d + f + b + s;
+        concat = "" + "S" + z + z + b + c + f + d + f;
+        concat = "" + b + c + b + s + S + "S" + c + f;
+        concat = "" + "S" + s + S + l + d + "S" + "S" + l;
+        concat = "" + l + S + l + l + f + c + s + d;
+        concat = "" + s + S + c + d + c + s + l + d;
+        concat = "" + z + i + d + b + S + c + d + d;
+        concat = "" + S + d + z + b + z + z + b + l;
+        concat = "" + z + d + "S" + i + i + s + l + "S";
+        concat = "" + S + b + S + l + d + i + l + i;
+        concat = "" + d + z + "S" + l + b + "S" + i + f;
+        concat = "" + d + s + b + d + i + b + z + l;
+        concat = "" + l + s + i + i + f + f + "S" + f;
+        concat = "" + s + i + "S" + "S" + c + i + f + b;
+        concat = "" + i + b + l + S + i + c + s + c;
+        concat = "" + s + c + i + "S" + i + f + c + S;
+        concat = "" + "S" + c + l + b + i + f + c + b;
+        concat = "" + z + c + i + "S" + l + S + S + S;
+        concat = "" + S + "S" + s + s + i + f + z + S;
+        concat = "" + c + S + S + c + f + l + f + d;
+        concat = "" + c + i + i + l + d + f + d + i;
+        concat = "" + f + d + f + z + f + "S" + "S" + f;
+        concat = "" + S + z + z + s + z + i + "S" + b;
+        concat = "" + z + c + i + i + d + z + l + i;
+        concat = "" + f + b + s + z + i + i + "S" + d;
+        concat = "" + d + s + l + z + S + c + S + i;
+        concat = "" + s + f + b + S + s + c + f + z;
+        concat = "" + z + b + f + d + "S" + z + b + s;
+        concat = "" + b + l + z + S + S + z + i + b;
+        concat = "" + i + d + f + c + "S" + b + s + d;
+        concat = "" + l + d + f + c + f + l + z + f;
+        concat = "" + i + l + c + l + c + i + S + z;
+        concat = "" + d + i + l + f + z + b + l + c;
+        concat = "" + d + "S" + S + f + c + c + f + i;
+        concat = "" + b + "S" + d + z + l + f + c + f;
+        concat = "" + d + c + i + "S" + z + b + l + "S";
+        concat = "" + l + d + c + s + f + "S" + s + s;
+        concat = "" + i + S + l + i + s + b + d + i;
+        concat = "" + c + S + b + s + d + l + i + l;
+        concat = "" + d + d + z + c + l + i + z + b;
+        concat = "" + i + l + z + S + b + "S" + i + s;
+        concat = "" + z + b + f + "S" + "S" + z + z + b;
+        concat = "" + c + l + l + s + S + c + f + c;
+        concat = "" + d + c + z + l + "S" + f + d + d;
+        concat = "" + "S" + i + b + l + c + s + i + c;
+        concat = "" + b + s + b + i + b + f + S + l;
+        concat = "" + l + S + z + s + f + z + s + d;
+        concat = "" + c + b + S + b + f + z + s + "S";
+        concat = "" + i + s + d + z + "S" + b + z + l;
+        concat = "" + c + d + i + f + i + l + d + f;
+        concat = "" + "S" + "S" + z + z + f + d + d + c;
+        concat = "" + z + s + S + f + S + S + i + "S";
+        concat = "" + l + d + z + b + i + d + "S" + c;
+        concat = "" + "S" + f + s + f + b + d + f + i;
+        concat = "" + i + z + f + l + z + d + "S" + S;
+        concat = "" + b + l + S + i + i + s + d + i;
+        concat = "" + i + f + d + "S" + f + s + s + s;
+        concat = "" + i + s + "S" + i + S + d + c + s;
+        concat = "" + i + b + "S" + d + s + "S" + i + i;
+        concat = "" + d + i + d + b + i + i + S + s;
+        concat = "" + "S" + z + c + S + S + b + i + l;
+        concat = "" + S + i + "S" + c + f + S + s + c;
+        concat = "" + "S" + l + l + b + S + s + "S" + l;
+        concat = "" + s + S + "S" + b + d + "S" + "S" + d;
+        concat = "" + i + i + z + z + d + i + s + i;
+        concat = "" + f + S + s + c + z + "S" + i + l;
+        concat = "" + S + i + z + z + S + f + f + c;
+        concat = "" + l + S + l + l + c + z + S + s;
+        concat = "" + f + s + f + c + f + s + z + z;
+        concat = "" + s + c + s + s + S + d + f + s;
+        concat = "" + c + "S" + d + z + i + b + "S" + "S";
+        concat = "" + S + b + i + f + l + d + s + s;
+        concat = "" + c + S + b + f + s + d + i + i;
+        concat = "" + l + d + i + f + s + s + b + d;
+        concat = "" + f + "S" + i + S + f + c + "S" + s;
+        concat = "" + s + "S" + i + f + z + i + l + c;
+        concat = "" + b + i + s + "S" + l + l + "S" + b;
+        concat = "" + b + d + S + "S" + S + b + s + c;
+        concat = "" + "S" + d + s + b + s + f + S + c;
+        concat = "" + i + l + S + i + d + d + d + b;
+        concat = "" + S + f + c + b + b + f + s + b;
+        concat = "" + d + i + S + z + f + l + z + s;
+        concat = "" + z + S + "S" + f + b + c + s + d;
+        concat = "" + c + f + b + z + b + c + "S" + d;
+        concat = "" + i + d + z + i + "S" + b + S + c;
+        concat = "" + s + l + s + S + c + b + l + z;
+        concat = "" + f + l + z + l + "S" + b + b + i;
+        concat = "" + s + d + "S" + i + S + b + z + c;
+        concat = "" + "S" + d + b + l + l + s + S + l;
+        concat = "" + b + z + c + s + c + l + i + l;
+        concat = "" + z + d + s + S + c + f + S + c;
+        concat = "" + z + s + f + S + b + b + s + b;
+        concat = "" + S + d + l + b + "S" + "S" + c + S;
+        concat = "" + S + z + s + z + d + "S" + i + z;
+        concat = "" + c + l + f + c + f + b + d + b;
+        concat = "" + f + c + s + l + i + l + s + S;
+        concat = "" + b + S + "S" + f + i + c + "S" + c;
+        concat = "" + s + c + l + f + z + b + f + z;
+        concat = "" + s + z + b + c + l + d + l + b;
+        concat = "" + s + d + f + l + i + s + S + S;
+        concat = "" + l + "S" + d + z + "S" + i + c + l;
+        concat = "" + i + c + z + d + s + b + S + s;
+        concat = "" + z + l + b + S + b + s + l + f;
+        concat = "" + i + z + f + d + f + b + s + z;
+        concat = "" + f + s + S + i + S + i + S + z;
+        concat = "" + d + s + l + i + c + "S" + "S" + f;
+        concat = "" + f + d + z + s + d + c + s + d;
+        concat = "" + b + S + b + l + c + "S" + l + b;
+        concat = "" + b + "S" + s + d + b + S + c + S;
+        concat = "" + l + l + i + b + d + z + z + l;
+        concat = "" + z + "S" + l + S + c + l + l + c;
+        concat = "" + S + z + "S" + z + S + c + c + b;
+        concat = "" + f + l + c + d + d + s + d + S;
+        concat = "" + f + z + z + d + z + l + d + S;
+        concat = "" + s + l + c + l + S + f + S + l;
+        concat = "" + c + f + d + l + "S" + b + b + b;
+        concat = "" + S + b + i + S + i + s + z + c;
+        concat = "" + z + c + z + S + s + f + f + f;
+        concat = "" + d + d + f + l + z + "S" + "S" + i;
+        concat = "" + f + f + b + c + b + c + f + z;
+        concat = "" + z + f + z + d + i + b + i + b;
+        concat = "" + S + "S" + i + c + b + s + i + z;
+        concat = "" + f + "S" + c + c + b + z + i + i;
+        concat = "" + "S" + "S" + d + i + "S" + d + i + "S";
+        concat = "" + b + i + S + i + S + z + d + d;
+        concat = "" + z + z + "S" + i + b + s + S + b;
+        concat = "" + z + "S" + z + "S" + i + c + S + s;
+        concat = "" + c + c + b + S + b + c + i + f;
+        concat = "" + b + i + i + i + i + d + b + b;
+        concat = "" + d + s + d + "S" + z + l + z + S;
+        concat = "" + "S" + s + d + l + f + c + d + "S";
+        concat = "" + i + S + b + s + d + l + l + "S";
+        concat = "" + "S" + d + s + b + S + s + S + z;
+        concat = "" + l + "S" + "S" + d + "S" + z + c + z;
+        concat = "" + i + d + i + f + f + c + "S" + "S";
+        concat = "" + l + d + l + "S" + c + l + S + "S";
+        concat = "" + "S" + l + i + d + c + l + d + S;
+        concat = "" + b + f + i + c + z + d + i + f;
+        concat = "" + S + b + s + f + s + b + "S" + "S";
+        concat = "" + z + d + b + z + l + s + z + S;
+        concat = "" + b + c + z + "S" + d + d + l + l;
+        concat = "" + "S" + i + i + "S" + f + s + c + c;
+        concat = "" + s + i + i + s + S + z + "S" + l;
+        concat = "" + s + i + f + s + z + c + l + f;
+        concat = "" + i + z + z + d + s + z + f + b;
+        concat = "" + "S" + l + s + d + f + "S" + i + c;
+        concat = "" + i + "S" + z + z + z + b + f + l;
+        concat = "" + l + S + S + "S" + c + l + f + "S";
+        concat = "" + b + f + c + c + "S" + S + b + c;
+        concat = "" + c + i + b + c + b + z + s + "S";
+        concat = "" + f + S + d + f + i + S + S + S;
+        concat = "" + f + d + S + c + b + s + i + b;
+        concat = "" + b + c + z + i + f + d + d + s;
+        concat = "" + "S" + c + z + l + "S" + i + s + l;
+        concat = "" + c + f + "S" + s + c + "S" + "S" + d;
+        concat = "" + f + c + "S" + d + f + i + z + i;
+        concat = "" + l + c + z + b + d + l + i + s;
+        concat = "" + c + d + l + d + i + b + S + b;
+        concat = "" + d + S + z + l + c + l + b + z;
+        concat = "" + "S" + l + f + i + c + S + i + c;
+        concat = "" + f + c + i + "S" + z + c + c + z;
+        concat = "" + "S" + S + i + i + d + b + "S" + z;
+        concat = "" + l + c + c + S + l + c + s + S;
+        concat = "" + s + z + z + s + d + z + S + i;
+        concat = "" + l + S + d + c + i + f + "S" + "S";
+        concat = "" + z + s + S + s + d + z + l + d;
+        concat = "" + d + c + l + i + "S" + f + b + c;
+        concat = "" + f + i + f + c + b + f + f + i;
+        concat = "" + "S" + S + S + "S" + z + d + l + l;
+        concat = "" + s + S + s + z + "S" + i + d + f;
+        concat = "" + d + c + l + S + f + i + S + S;
+        concat = "" + "S" + f + d + c + i + i + d + f;
+        concat = "" + l + f + z + f + S + c + d + S;
+        concat = "" + i + f + "S" + b + s + b + f + "S";
+        concat = "" + l + "S" + z + i + b + c + b + l;
+        concat = "" + l + S + s + "S" + f + f + z + l;
+        concat = "" + "S" + c + "S" + f + i + s + "S" + f;
+        concat = "" + f + s + s + i + b + S + d + b;
+        concat = "" + s + f + "S" + b + f + f + i + z;
+        concat = "" + s + s + S + b + "S" + s + z + z;
+        concat = "" + S + f + b + l + z + s + f + S;
+        concat = "" + c + s + s + c + S + s + f + c;
+        concat = "" + i + f + l + d + "S" + f + i + d;
+        concat = "" + l + "S" + S + c + f + b + l + d;
+        concat = "" + S + s + S + S + f + c + d + S;
+        concat = "" + s + l + c + l + S + c + "S" + S;
+        concat = "" + i + i + l + z + "S" + i + b + b;
+        concat = "" + c + i + c + S + "S" + i + s + b;
+        concat = "" + l + i + c + i + z + c + d + f;
+        concat = "" + f + z + S + i + i + z + z + d;
+        concat = "" + l + f + z + f + c + S + l + c;
+        concat = "" + "S" + s + d + l + c + s + z + d;
+        concat = "" + S + s + s + z + c + b + z + i;
+        concat = "" + f + b + z + f + z + "S" + s + z;
+        concat = "" + "S" + S + i + d + f + d + f + d;
+        concat = "" + i + l + s + l + b + z + f + i;
+        concat = "" + f + z + l + f + z + z + b + d;
+        concat = "" + S + i + z + l + b + i + z + d;
+        concat = "" + i + i + f + f + l + d + b + b;
+        concat = "" + d + S + i + f + S + S + c + l;
+        concat = "" + "S" + l + "S" + S + i + b + z + l;
+        concat = "" + d + l + c + b + s + "S" + s + b;
+        concat = "" + c + s + l + f + c + l + "S" + b;
+        concat = "" + i + "S" + z + l + z + s + s + "S";
+        concat = "" + f + b + f + b + c + c + "S" + z;
+        concat = "" + z + z + i + s + i + b + "S" + z;
+        concat = "" + c + l + l + s + s + "S" + z + "S";
+        concat = "" + i + i + d + s + z + l + b + c;
+        concat = "" + f + l + l + S + "S" + i + z + "S";
+        concat = "" + s + s + i + f + "S" + s + z + c;
+        concat = "" + l + f + i + s + d + l + b + b;
+        concat = "" + "S" + s + b + l + z + i + f + s;
+        concat = "" + z + l + b + b + d + z + "S" + "S";
+        concat = "" + d + s + s + l + f + z + "S" + s;
+        concat = "" + l + i + "S" + z + d + f + "S" + s;
+        concat = "" + f + d + i + "S" + s + S + i + i;
+        concat = "" + i + b + s + S + i + s + i + i;
+        concat = "" + S + "S" + d + b + z + i + S + c;
+        concat = "" + f + S + i + s + s + z + f + c;
+        concat = "" + c + z + l + S + d + b + f + "S";
+        concat = "" + d + l + S + f + z + S + c + c;
+        concat = "" + "S" + b + S + i + l + d + c + c;
+        concat = "" + "S" + S + S + d + i + f + S + l;
+        concat = "" + c + "S" + f + "S" + z + d + d + l;
+        concat = "" + S + l + b + S + c + b + z + b;
+        concat = "" + i + l + z + l + b + d + d + l;
+        concat = "" + c + c + f + b + S + s + z + i;
+        concat = "" + l + l + "S" + i + "S" + s + "S" + c;
+        concat = "" + "S" + c + i + s + i + d + b + d;
+        concat = "" + z + l + i + i + b + l + f + S;
+        concat = "" + d + S + "S" + b + b + l + c + c;
+        concat = "" + S + "S" + z + z + s + f + c + b;
+        concat = "" + l + s + d + c + s + S + c + z;
+        concat = "" + f + i + b + f + i + b + c + i;
+        concat = "" + d + f + d + S + d + "S" + s + f;
+        concat = "" + c + i + b + S + "S" + z + s + s;
+        concat = "" + b + s + "S" + S + c + d + b + d;
+        concat = "" + s + s + f + i + l + S + S + S;
+        concat = "" + d + b + d + l + S + z + b + S;
+        concat = "" + d + d + i + b + z + "S" + S + c;
+        concat = "" + z + f + s + "S" + d + S + f + b;
+        concat = "" + d + "S" + f + f + z + f + S + z;
+        concat = "" + i + "S" + S + i + "S" + d + "S" + "S";
+        concat = "" + "S" + S + c + z + l + c + z + d;
+        concat = "" + l + z + c + i + s + i + l + S;
+        concat = "" + b + s + z + "S" + b + l + s + l;
+        concat = "" + d + f + i + b + c + f + s + f;
+        concat = "" + S + f + l + s + d + S + i + c;
+        concat = "" + "S" + s + "S" + S + z + "S" + f + d;
+        concat = "" + c + z + i + S + c + "S" + d + l;
+        concat = "" + c + "S" + c + c + c + "S" + i + b;
+        concat = "" + c + l + f + d + d + c + i + i;
+        concat = "" + i + S + s + S + f + c + d + b;
+        concat = "" + i + i + S + b + l + l + "S" + c;
+        concat = "" + d + z + "S" + d + i + c + b + b;
+        concat = "" + c + b + S + b + b + "S" + "S" + S;
+        concat = "" + c + i + l + i + d + z + "S" + l;
+        concat = "" + S + d + "S" + d + b + d + s + i;
+        concat = "" + z + f + S + c + c + "S" + d + "S";
+        concat = "" + c + z + c + b + l + s + "S" + b;
+        concat = "" + S + s + "S" + "S" + c + l + s + f;
+        concat = "" + S + "S" + l + c + c + f + b + S;
+        concat = "" + c + f + S + f + z + z + f + z;
+        concat = "" + d + l + z + i + f + f + b + l;
+        concat = "" + "S" + S + f + "S" + s + s + s + z;
+        concat = "" + d + b + l + f + b + z + z + b;
+        concat = "" + S + z + i + d + l + l + l + b;
+        concat = "" + l + b + i + S + c + f + i + i;
+        concat = "" + c + l + z + d + "S" + d + s + S;
+        concat = "" + l + i + i + l + b + z + d + f;
+        concat = "" + S + s + c + i + z + d + d + i;
+        concat = "" + z + s + b + z + l + z + d + d;
+        concat = "" + s + d + S + S + s + c + S + "S";
+        concat = "" + b + b + s + s + S + l + f + z;
+        concat = "" + l + "S" + i + c + i + s + "S" + "S";
+        concat = "" + s + l + "S" + S + "S" + S + S + c;
+        concat = "" + i + z + b + l + z + c + c + s;
+        concat = "" + d + d + i + b + l + "S" + S + i;
+        concat = "" + "S" + l + f + c + l + "S" + z + i;
+        concat = "" + "S" + "S" + "S" + l + S + b + s + z;
+        concat = "" + c + d + S + d + S + z + f + "S";
+        concat = "" + c + s + S + b + s + S + c + l;
+        concat = "" + "S" + b + S + S + d + c + f + b;
+        concat = "" + f + l + c + S + z + b + i + c;
+        concat = "" + "S" + f + i + l + S + d + i + f;
+        concat = "" + d + z + s + d + S + s + z + c;
+        concat = "" + b + b + z + z + S + d + S + S;
+        concat = "" + s + d + b + c + z + s + f + c;
+        concat = "" + c + z + S + l + "S" + z + l + S;
+        concat = "" + c + i + "S" + "S" + z + s + i + "S";
+        concat = "" + b + c + s + s + b + "S" + "S" + "S";
+        concat = "" + "S" + S + l + z + f + i + l + i;
+        concat = "" + S + c + c + S + c + i + b + i;
+        concat = "" + d + b + f + S + l + l + "S" + b;
+        concat = "" + d + "S" + s + d + c + s + f + l;
+        concat = "" + l + s + S + s + z + d + i + z;
+        concat = "" + b + "S" + i + f + b + d + i + i;
+        concat = "" + c + i + b + s + "S" + f + l + d;
+        concat = "" + "S" + b + d + i + f + f + S + l;
+        concat = "" + f + s + b + c + b + i + c + i;
+        concat = "" + s + d + z + d + b + d + c + S;
+        concat = "" + c + "S" + f + c + S + b + b + d;
+        concat = "" + f + d + l + f + f + S + c + i;
+        concat = "" + l + b + i + i + c + b + l + "S";
+        concat = "" + f + d + b + "S" + c + s + z + c;
+        concat = "" + i + b + d + s + c + f + s + c;
+        concat = "" + d + f + S + "S" + S + s + d + i;
+        concat = "" + s + b + l + z + i + s + i + l;
+        concat = "" + b + b + z + c + l + f + z + f;
+        concat = "" + i + d + "S" + z + "S" + i + s + i;
+        concat = "" + i + b + c + d + c + "S" + s + l;
+        concat = "" + f + c + d + f + f + f + z + l;
+        concat = "" + f + s + b + z + l + c + s + c;
+        concat = "" + b + i + "S" + "S" + "S" + c + d + d;
+        concat = "" + s + i + z + z + s + S + S + c;
+        concat = "" + f + S + f + S + S + z + "S" + f;
+        concat = "" + i + "S" + d + i + S + f + z + d;
+        concat = "" + f + c + s + d + S + f + b + b;
+        concat = "" + s + d + b + s + s + S + b + f;
+        concat = "" + c + d + s + "S" + f + i + l + "S";
+        concat = "" + i + s + b + z + S + s + f + "S";
+        concat = "" + i + S + "S" + i + b + z + S + b;
+        concat = "" + c + S + s + s + z + S + z + i;
+        concat = "" + s + d + S + c + "S" + f + S + "S";
+        concat = "" + "S" + c + "S" + l + s + b + z + S;
+        concat = "" + s + d + d + f + f + s + z + z;
+        concat = "" + d + d + "S" + c + s + b + "S" + l;
+        concat = "" + i + f + S + c + d + d + d + f;
+        concat = "" + i + l + d + i + b + f + l + f;
+        concat = "" + s + i + s + l + l + S + d + f;
+        concat = "" + l + "S" + f + d + c + b + l + c;
+        concat = "" + i + b + c + i + l + "S" + c + b;
+        concat = "" + s + s + f + l + c + "S" + s + s;
+        concat = "" + d + c + d + S + S + S + s + l;
+        concat = "" + i + "S" + b + s + c + f + c + z;
+        concat = "" + S + d + d + s + S + d + l + f;
+        concat = "" + c + l + c + f + l + d + "S" + f;
+        concat = "" + i + d + "S" + i + S + S + f + S;
+        concat = "" + "S" + s + z + s + b + d + d + c;
+        concat = "" + l + z + s + f + "S" + f + f + d;
+        concat = "" + "S" + l + i + "S" + d + s + S + s;
+        concat = "" + d + "S" + l + S + i + "S" + b + "S";
+        concat = "" + S + S + l + d + z + s + S + b;
+        concat = "" + s + s + f + s + i + b + s + l;
+        concat = "" + "S" + b + i + "S" + z + z + S + b;
+        concat = "" + i + c + l + s + b + d + s + S;
+        concat = "" + d + l + b + z + f + d + c + i;
+        concat = "" + s + b + i + i + s + b + z + c;
+        concat = "" + i + "S" + s + c + l + i + c + d;
+        concat = "" + f + c + "S" + b + d + i + l + b;
+        concat = "" + "S" + z + d + s + f + s + s + d;
+        concat = "" + i + l + z + d + S + "S" + d + f;
+        concat = "" + s + f + "S" + l + s + f + b + "S";
+        concat = "" + S + b + z + i + i + l + i + b;
+        concat = "" + d + b + f + d + d + c + b + z;
+        concat = "" + d + S + d + c + l + f + S + S;
+        concat = "" + S + s + z + b + s + "S" + s + z;
+        concat = "" + c + S + S + d + l + S + S + f;
+        concat = "" + f + d + f + "S" + f + "S" + b + s;
+        concat = "" + s + S + "S" + s + f + b + S + c;
+        concat = "" + i + s + S + d + S + s + f + s;
+        concat = "" + i + S + b + d + d + d + c + s;
+        concat = "" + S + S + f + b + S + c + f + "S";
+        concat = "" + l + b + z + f + i + c + l + f;
+        concat = "" + "S" + c + S + c + l + f + z + z;
+        concat = "" + S + "S" + l + i + b + z + l + z;
+        concat = "" + z + "S" + i + c + f + z + "S" + z;
+        concat = "" + c + f + f + l + i + i + i + d;
+        concat = "" + S + s + d + "S" + i + l + b + c;
+        concat = "" + l + d + s + c + c + d + f + S;
+        concat = "" + f + "S" + S + c + c + l + z + S;
+        concat = "" + z + d + c + c + i + c + d + "S";
+        concat = "" + b + S + z + b + i + "S" + "S" + "S";
+        concat = "" + d + f + "S" + l + s + z + d + S;
+        concat = "" + d + S + b + f + s + l + i + i;
+        concat = "" + b + z + S + l + i + l + S + S;
+        concat = "" + i + l + d + s + z + l + s + "S";
+        concat = "" + S + S + s + b + b + z + s + "S";
+        concat = "" + "S" + S + S + l + l + z + "S" + i;
+        concat = "" + d + b + l + b + l + d + c + z;
+        concat = "" + "S" + i + s + d + c + S + c + s;
+        concat = "" + z + c + S + d + c + c + l + i;
+        concat = "" + d + d + l + f + S + z + d + "S";
+        concat = "" + s + f + S + z + "S" + b + c + z;
+        concat = "" + "S" + c + c + d + "S" + S + d + S;
+        concat = "" + c + S + i + f + c + i + s + S;
+        concat = "" + "S" + S + S + c + c + i + s + l;
+        concat = "" + S + d + d + b + f + c + "S" + s;
+        concat = "" + f + z + f + i + "S" + i + "S" + s;
+        concat = "" + "S" + f + S + c + l + S + d + s;
+        concat = "" + S + i + c + c + "S" + c + i + l;
+        concat = "" + i + "S" + c + d + f + z + S + S;
+        concat = "" + b + f + c + l + s + d + c + S;
+        concat = "" + l + S + i + i + z + l + "S" + f;
+        concat = "" + z + "S" + d + i + f + i + b + b;
+        concat = "" + "S" + l + f + "S" + b + f + "S" + s;
+        concat = "" + i + s + b + b + i + f + s + b;
+        concat = "" + l + d + i + f + l + i + S + "S";
+        concat = "" + f + b + S + S + c + b + s + l;
+        concat = "" + i + s + f + S + s + S + s + b;
+        concat = "" + f + "S" + z + "S" + s + f + c + s;
+        concat = "" + "S" + S + b + z + f + i + s + s;
+        concat = "" + b + s + l + z + "S" + i + s + S;
+        concat = "" + i + c + z + s + S + d + l + z;
+        concat = "" + s + "S" + b + c + "S" + c + d + l;
+        concat = "" + c + b + b + i + c + S + b + z;
+        concat = "" + S + c + "S" + S + z + s + "S" + s;
+        concat = "" + b + f + d + "S" + s + S + d + S;
+        concat = "" + f + d + b + i + S + f + d + d;
+        concat = "" + s + c + "S" + s + s + l + s + z;
+        concat = "" + f + d + z + c + i + s + z + d;
+        concat = "" + i + s + c + f + b + z + S + f;
+        concat = "" + S + d + z + i + i + b + i + z;
+        concat = "" + d + b + S + d + d + i + b + s;
+        concat = "" + d + "S" + f + c + l + c + c + d;
+        concat = "" + z + z + S + S + f + d + i + c;
+        concat = "" + "S" + l + z + f + "S" + l + d + l;
+        concat = "" + f + l + d + b + b + "S" + l + i;
+        concat = "" + s + i + S + b + c + z + l + l;
+        concat = "" + z + f + S + i + l + z + l + S;
+        concat = "" + d + z + b + z + s + b + z + z;
+        concat = "" + d + b + d + f + d + i + d + s;
+        concat = "" + S + d + d + "S" + i + "S" + c + d;
+        concat = "" + f + f + c + l + f + d + s + d;
+        concat = "" + b + S + f + l + c + c + s + d;
+        concat = "" + S + s + s + d + f + "S" + i + i;
+        concat = "" + i + z + i + d + b + d + c + l;
+        concat = "" + i + S + f + S + f + d + d + "S";
+        concat = "" + i + "S" + i + l + s + l + i + b;
+        concat = "" + d + "S" + "S" + c + z + S + c + c;
+        concat = "" + s + l + f + l + d + "S" + l + s;
+        concat = "" + b + z + S + c + c + f + l + S;
+        concat = "" + z + i + f + s + d + i + S + c;
+        concat = "" + "S" + S + l + l + b + z + s + s;
+        concat = "" + f + z + z + l + d + c + l + d;
+        concat = "" + b + c + s + b + d + l + z + b;
+        concat = "" + z + f + c + s + z + b + c + c;
+        concat = "" + i + b + b + c + c + d + b + f;
+        concat = "" + d + f + d + f + "S" + S + "S" + d;
+        concat = "" + z + z + f + i + d + i + f + "S";
+        concat = "" + i + s + "S" + d + l + d + z + b;
+        concat = "" + "S" + "S" + c + i + S + f + b + S;
+        concat = "" + z + s + b + S + c + c + s + b;
+        concat = "" + z + "S" + f + c + c + i + f + z;
+        concat = "" + b + d + d + f + i + s + i + d;
+        concat = "" + f + i + s + c + z + s + "S" + f;
+        concat = "" + S + s + l + b + s + "S" + "S" + b;
+        concat = "" + c + b + b + i + "S" + S + b + S;
+        concat = "" + d + s + S + i + f + f + c + S;
+        concat = "" + f + z + "S" + f + c + b + d + S;
+        concat = "" + l + "S" + l + s + c + s + c + i;
+        concat = "" + S + b + S + c + c + l + l + S;
+        concat = "" + i + "S" + l + b + f + s + i + s;
+        concat = "" + S + z + b + S + c + c + l + c;
+        concat = "" + S + s + i + b + z + d + l + c;
+        concat = "" + f + c + i + i + z + f + S + b;
+        concat = "" + z + l + "S" + z + i + "S" + i + b;
+        concat = "" + i + S + z + b + l + i + i + d;
+        concat = "" + d + z + d + b + b + c + d + z;
+        concat = "" + l + i + "S" + i + "S" + l + b + i;
+        concat = "" + f + c + l + z + S + f + s + "S";
+        concat = "" + b + i + z + "S" + d + b + d + l;
+        concat = "" + c + z + c + z + "S" + S + l + s;
+        concat = "" + f + f + s + b + s + l + c + s;
+        concat = "" + "S" + c + S + l + f + S + z + c;
+        concat = "" + i + l + f + f + f + c + b + l;
+        concat = "" + d + d + "S" + c + S + l + c + z;
+        concat = "" + i + d + f + i + S + S + "S" + s;
+        concat = "" + f + i + z + i + s + b + S + c;
+        concat = "" + z + l + s + "S" + s + i + b + f;
+        concat = "" + f + s + c + "S" + f + l + z + f;
+        concat = "" + z + l + S + c + c + "S" + d + f;
+        concat = "" + l + z + l + s + d + b + i + "S";
+        concat = "" + l + f + d + z + z + b + b + d;
+        concat = "" + l + S + f + z + f + l + c + z;
+        concat = "" + s + "S" + z + b + z + c + b + z;
+        concat = "" + d + c + S + s + l + i + b + s;
+        concat = "" + "S" + b + s + z + f + S + l + "S";
+        concat = "" + l + f + b + i + z + f + i + s;
+        concat = "" + f + i + S + d + f + z + S + S;
+        concat = "" + d + i + c + f + s + "S" + f + s;
+        concat = "" + z + l + s + s + z + f + l + i;
+        concat = "" + l + z + b + l + i + f + i + c;
+        concat = "" + d + s + b + d + c + l + l + b;
+        concat = "" + i + b + d + d + d + c + s + i;
+        concat = "" + z + "S" + f + b + i + z + i + b;
+        concat = "" + s + S + c + S + c + S + "S" + b;
+        concat = "" + i + b + b + d + i + l + d + s;
+        concat = "" + l + S + c + z + s + z + S + "S";
+        concat = "" + S + c + l + b + b + d + f + b;
+        concat = "" + "S" + s + l + z + l + f + z + s;
+        concat = "" + S + s + s + z + d + d + S + d;
+        concat = "" + f + "S" + l + s + i + s + b + S;
+        concat = "" + f + f + "S" + i + l + c + f + c;
+        concat = "" + s + "S" + "S" + "S" + z + b + f + f;
+        concat = "" + f + s + l + f + b + c + s + i;
+        concat = "" + i + f + S + "S" + z + z + i + s;
+        concat = "" + d + "S" + c + "S" + z + z + c + z;
+        concat = "" + d + b + d + b + d + c + z + l;
+        concat = "" + b + z + z + S + S + b + b + i;
+        concat = "" + b + c + d + s + b + f + S + c;
+        concat = "" + i + S + l + f + c + "S" + c + z;
+        concat = "" + d + z + f + S + i + S + l + s;
+        concat = "" + z + l + i + z + l + i + l + f;
+        concat = "" + b + d + s + "S" + f + c + d + s;
+        concat = "" + "S" + b + c + l + d + b + S + c;
+        concat = "" + S + d + "S" + "S" + f + S + f + b;
+        concat = "" + f + z + s + f + "S" + c + d + S;
+        concat = "" + S + d + S + S + S + i + i + f;
+        concat = "" + b + i + c + z + s + l + d + S;
+        concat = "" + "S" + s + z + "S" + i + i + S + z;
+        concat = "" + "S" + l + z + f + "S" + d + S + i;
+        concat = "" + b + i + f + z + "S" + s + "S" + z;
+        concat = "" + f + i + f + i + b + c + d + "S";
+        concat = "" + s + s + c + d + b + s + S + l;
+        concat = "" + l + S + d + d + i + z + f + d;
+        concat = "" + z + f + "S" + S + c + i + s + i;
+        concat = "" + l + s + f + i + s + b + s + s;
+        concat = "" + "S" + l + c + S + l + c + f + z;
+        concat = "" + S + c + s + s + f + l + b + b;
+        concat = "" + f + "S" + S + s + S + i + s + "S";
+        concat = "" + d + d + z + f + i + s + b + f;
+        concat = "" + "S" + l + f + z + i + s + b + d;
+        concat = "" + z + "S" + s + f + l + "S" + "S" + b;
+        concat = "" + S + z + z + s + f + "S" + l + b;
+        concat = "" + "S" + z + i + s + d + f + f + l;
+        concat = "" + S + c + s + s + S + d + s + i;
+        concat = "" + d + b + "S" + s + c + z + c + b;
+        concat = "" + z + l + f + S + "S" + "S" + l + b;
+        concat = "" + d + d + f + "S" + "S" + b + c + i;
+        concat = "" + f + d + s + S + "S" + l + i + l;
+        concat = "" + b + c + b + b + b + c + s + "S";
+        concat = "" + f + c + d + "S" + z + s + b + b;
+        concat = "" + d + d + i + z + "S" + b + i + c;
+        concat = "" + d + s + c + s + "S" + s + f + b;
+        concat = "" + z + i + l + c + b + "S" + f + s;
+        concat = "" + b + S + c + b + l + s + i + i;
+        concat = "" + d + f + c + z + S + S + S + s;
+        concat = "" + z + z + z + c + S + c + i + i;
+        concat = "" + b + i + b + f + S + i + f + d;
+        concat = "" + c + f + b + i + "S" + s + "S" + c;
+        concat = "" + b + i + d + b + "S" + d + z + "S";
+        concat = "" + z + s + s + "S" + b + d + z + "S";
+        concat = "" + z + l + S + z + S + "S" + S + d;
+        concat = "" + l + f + l + S + l + "S" + f + d;
+        concat = "" + z + i + c + l + d + f + s + s;
+        concat = "" + i + S + c + b + c + "S" + c + "S";
+        concat = "" + "S" + b + s + l + d + z + "S" + d;
+        concat = "" + i + l + i + S + d + "S" + c + f;
+        concat = "" + b + b + b + "S" + s + d + b + s;
+        concat = "" + "S" + b + d + i + S + "S" + c + S;
+        concat = "" + z + z + "S" + i + l + S + b + S;
+        concat = "" + "S" + s + c + f + f + s + d + s;
+        concat = "" + i + i + f + "S" + s + f + c + f;
+        concat = "" + l + c + s + i + d + "S" + z + l;
+        concat = "" + f + d + l + l + "S" + i + i + f;
+        concat = "" + z + l + d + c + f + i + z + i;
+        concat = "" + i + s + l + b + f + d + b + d;
+        concat = "" + z + b + d + z + b + S + l + i;
+        concat = "" + b + f + d + "S" + "S" + c + d + i;
+        concat = "" + s + c + "S" + "S" + b + d + d + S;
+        concat = "" + d + b + s + b + c + d + z + z;
+        concat = "" + b + c + z + S + d + S + S + i;
+        concat = "" + d + c + z + f + l + l + d + c;
+        concat = "" + i + l + b + b + l + S + b + b;
+        concat = "" + i + b + i + b + S + "S" + z + "S";
+        /* Keep this comment here so you can add a /* comment above to reduce the number of shapes in this file */
+        result = concat;
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/indy/StringConcatStress2.java b/test/hotspot/jtreg/runtime/cds/appcds/indy/StringConcatStress2.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/indy/StringConcatStress2.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2024, 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 id=static
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @build StringConcatStress2
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar StringConcatStress2App
+ * @run driver StringConcatStress2 STATIC
+ */
+
+/*
+ * @test id=leyden
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @build StringConcatStress2
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar StringConcatStress2App
+ * @run driver StringConcatStress2 LEYDEN
+ */
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class StringConcatStress2 {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "StringConcatStress2App";
+
+    public static void main(String[] args) throws Exception {
+        Tester t = new Tester();
+        t.run(args);
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar;
+        }
+
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-XX:+AOTClassLinking", // by default enables AOTInvokeDynamicLinking
+            };
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                mainClass
+            };
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (!runMode.isStaticDump()) {
+                out.shouldContain("Result = 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1END");
+            }
+        }
+    }
+}
+
+class StringConcatStress2App {
+    public static void main(String args[]) {
+        System.out.println("Result = " + concat23String() + "END");
+    }
+
+    // Test the handling of java.lang.invoke.StringConcat.highArityThreshold (default value is 20)
+    // in java.lang.invoke.StringConcatFactory. If we have more than this number of concats, a
+    // different form of CallSite is generated by StringConcatFactory that will uses
+    // different kinds of hidden classes than usual.
+    static String concat23String() {
+        return f0 + ","+ f1 + ","+ f2 + ","+ f3 + ","+ f4 + ","+ f5 + ","+ f6 + ","+ f7 + ","+ f8 + ","+ f9 + ","
+                + f10 + ","+ f11 + ","+ f12 + ","+ f13 + ","+ f14 + ","+ f15 + ","+ f16 + ","+ f17 + ","+ f18 + ","+ f19 + ","
+                + f20 + ","+ f21 + ","+ f22;
+    }
+
+    private static String
+            f0="1", f1="1", f2="1", f3="1", f4="1", f5="1", f6="1", f7="1", f8="1", f9="1",
+            f10="1", f11="1", f12="1", f13="1", f14="1", f15="1", f16="1", f17="1", f18="1", f19="1",
+            f20="1", f21="1", f22="1", f23="1", f24="1", f25="1", f26="1", f27="1", f28="1", f29="1",
+            f30="1", f31="1", f32="1", f33="1", f34="1", f35="1", f36="1", f37="1", f38="1", f39="1",
+            f40="1", f41="1", f42="1", f43="1", f44="1", f45="1", f46="1", f47="1", f48="1", f49="1",
+            f50="1", f51="1", f52="1", f53="1", f54="1", f55="1", f56="1", f57="1", f58="1", f59="1",
+            f60="1", f61="1", f62="1", f63="1", f64="1", f65="1", f66="1", f67="1", f68="1", f69="1",
+            f70="1", f71="1", f72="1", f73="1", f74="1", f75="1", f76="1", f77="1", f78="1", f79="1",
+            f80="1", f81="1", f82="1", f83="1", f84="1", f85="1", f86="1", f87="1", f88="1", f89="1",
+            f90="1", f91="1", f92="1", f93="1", f94="1", f95="1", f96="1", f97="1", f98="1", f99="1",
+            f100="1",f101="1",f102="1",f103="1",f104="1",f105="1",f106="1",f107="1",f108="1",f109="1",
+            f110="1",f111="1",f112="1",f113="1",f114="1",f115="1",f116="1",f117="1",f118="1",f119="1",
+            f120="1",f121="1",f122="1";
+
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/CacheOnlyClassesIn.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/CacheOnlyClassesIn.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/CacheOnlyClassesIn.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+// Experimental feature to allow special instrumentation code to be executed during training run:
+//
+// Example:  app.jar   -- Contains the classes for the real application.
+//                        Entry point = the "App" class
+//           test.jar  -- Instrumentation code to exercise the app or the framework. 
+//                        Entry point = the "TestDriver" class
+//
+// Training run:
+//
+//     rm app.cds
+//     java -XX:CacheDataStore=app.cds -cp app.jar:test.jar -XX:CacheOnlyClassesIn=app.jar TestDriver
+//
+// All classes in test.jar will be excluded from app.cds
+//
+// Production run:
+//
+//     java -XX:CacheDataStore=app.cds -cp app.jar:test.jar App
+//
+// TODO:
+//     Allow "-cp app.jar" to be used in production run, so test classes don't leaked into
+//     the production run.
+
+/*
+ * @test id=static
+ * @summary test the -XX:CacheOnlyClassesIn flag with the classic static archive workflow
+ * @requires vm.cds
+ * @library /test/lib
+ * @run driver CacheOnlyClassesIn STATIC
+ */
+
+/*
+ * @test id=dynamic
+ * @summary test the -XX:CacheOnlyClassesIn flag with the classic dynamic archive workflow
+ * @requires vm.cds
+ * @library /test/lib
+ * @run driver CacheOnlyClassesIn DYNAMIC
+ */
+
+/*
+ * @test id=leyden
+ * @summary test the -XX:CacheOnlyClassesIn flag with Leyden workflow
+ * @requires vm.cds
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/lib
+ * @run driver CacheOnlyClassesIn LEYDEN
+ */
+
+import java.io.File;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class CacheOnlyClassesIn {
+    static String mainClass = CacheOnlyClassesInApp.class.getName();
+    static String appJar, testJar;
+
+    public static void main(String args[]) throws Exception {
+        appJar = ClassFileInstaller.writeJar("app.jar",
+                                             "CacheOnlyClassesInApp");
+        testJar = ClassFileInstaller.writeJar("test.jar",
+                                             "TestHarness");
+        MyTester tester = new MyTester();
+        tester.run(args);
+    }
+
+    static class MyTester extends CDSAppTester {
+        public MyTester() {
+            super("CacheOnlyClassesInApp");
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar + File.pathSeparator + testJar;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-XX:CacheOnlyClassesIn=" + appJar,
+            };
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                mainClass,
+            };
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
+            if (runMode == RunMode.DUMP_STATIC ||
+                runMode == RunMode.DUMP_DYNAMIC ||
+                runMode == RunMode.TRAINING ||
+                runMode == RunMode.TRAINING0) {
+                out.shouldContain("Skipping TestHarness: excluded via -XX:CacheOnlyClassesIn");
+            }
+        }
+    }
+}
+
+class CacheOnlyClassesInApp {
+    public static void main(String args[]) {
+        System.out.println("Hello World = " + TestHarness.doit());
+    }
+
+    static volatile int x;
+    public static int testLoop() {
+        for (int i = 0; i < 100000000; i++) {
+            x ^= (x + 39);
+        }
+        return x;
+    }
+}
+
+class TestHarness {
+    static int doit() {
+        return CacheOnlyClassesInApp.testLoop() + myTestLoop();
+    }
+
+    static volatile int x;
+    public static int myTestLoop() {
+        for (int i = 0; i < 100000000; i++) {
+            x ^= (x + 39);
+        }
+        return x;
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/DynamicProxyTest.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/DynamicProxyTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/DynamicProxyTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2023, 2024, 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
+ * @summary Test for archiving dynamic proxies
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @build DynamicProxyTest testpkg.PublicTester
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app1.jar
+ *                 DynamicProxyApp MyInvocationHandler
+ *                 DynamicProxyTest$Foo DynamicProxyTest$Boo DynamicProxyTest$Coo
+ *                 DynamicProxyTest$DefaultMethodWithUnlinkedClass Fruit
+ *                 testpkg.PublicTester testpkg.NonPublicInterface testpkg.MyInvocationHandler2
+ *                 jdk.test.lib.Asserts 
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app2.jar
+ *                 Fruit Apple
+ * @run driver DynamicProxyTest LEYDEN
+ */
+
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.ProtectionDomain;
+import java.io.File;
+import java.util.logging.Filter;
+import java.util.Map;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class DynamicProxyTest {
+    static final String app1Jar = ClassFileInstaller.getJarPath("app1.jar");
+    static final String app2Jar = ClassFileInstaller.getJarPath("app2.jar");
+    static final String mainClass = "DynamicProxyApp";
+
+    public static void main(String[] args) throws Exception {
+        Tester t = new Tester();
+        t.run(args);
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super("DynamicProxyTest");
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            if (runMode == RunMode.CLASSLIST || runMode == RunMode.DUMP_STATIC) {
+                return app1Jar;
+            } else {
+                return app1Jar + File.pathSeparator + app2Jar;
+            }
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                "DynamicProxyApp", runMode.name()
+            };
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-XX:+ArchiveDynamicProxies",
+                "-Xlog:cds+dynamic+proxy:file=DynamicProxyTest.proxies." + runMode + ".log"
+            };
+        }
+    }
+
+    public static interface Foo {
+        public Object bar();
+    }
+
+    public static interface Boo {
+        public Object far();
+    }
+
+    static interface Coo {
+        public Object car();
+    }
+
+    public static interface DefaultMethodWithUnlinkedClass {
+        default Object doit(boolean flag) {
+            if (flag) {
+                Fruit f = Fruit.get();
+                f.peel();
+                return f;
+            } else {
+                return "Do not link Fruit class";
+            }
+        }
+    }
+}
+
+class MyInvocationHandler implements InvocationHandler {
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        if (method.getName().equals("isLoggable")) {
+            return Boolean.TRUE;
+        } else {
+            return method.getName() + "() in " + proxy.getClass() + " is called";
+        }
+    }
+}
+
+class DynamicProxyApp {
+    static String[] classes = {
+        "java.io.Serializable",
+        "java.lang.annotation.Documented",
+        "java.lang.annotation.Inherited",
+        "java.lang.annotation.Retention",
+        "java.lang.annotation.Target",
+        "java.lang.Enum",
+        "java.lang.Integer",
+        "java.lang.management.BufferPoolMXBean",
+        "java.lang.management.ClassLoadingMXBean",
+        "java.lang.management.CompilationMXBean",
+        "java.lang.management.GarbageCollectorMXBean",
+        "java.lang.management.MemoryManagerMXBean",
+        "java.lang.management.MemoryMXBean",
+        "java.lang.management.MemoryPoolMXBean",
+        "java.lang.management.OperatingSystemMXBean",
+        "java.lang.management.PlatformLoggingMXBean",
+        "java.lang.management.PlatformManagedObject",
+        "java.lang.management.RuntimeMXBean",
+        "java.lang.management.ThreadMXBean",
+        "java.lang.Number",
+        "java.lang.Object",
+        "java.lang.Package$1PackageInfoProxy",
+        "java.lang.reflect.Proxy",
+        "java.lang.String",
+        "java.time.LocalDate",
+        "java.util.Collections$UnmodifiableMap",
+        "java.util.Dictionary",
+        "java.util.Hashtable",
+        "java.util.Properties",
+        "javax.management.NotificationBroadcaster",
+        "javax.management.NotificationEmitter",
+        "javax.naming.Referenceable",
+        "javax.sql.CommonDataSource",
+        "javax.sql.DataSource",
+        "jdk.management.jfr.FlightRecorderMXBean",
+    };
+
+    static void checkRead(boolean expected, Module proxyModule, Module targetModule) {
+        Asserts.assertEQ(proxyModule.canRead(targetModule), expected,
+                         "(" + proxyModule + ") canRead (" +
+                         targetModule + " of loader " + targetModule.getClassLoader() + ") = " +
+                         proxyModule.canRead(targetModule) + ", but should be " + expected);
+    }
+
+    static boolean hasArchivedProxies;
+
+    private static void checkArchivedProxy(Class c, boolean shouldBeArchived) {
+        if (hasArchivedProxies) {
+            // We can't use WhiteBox, which would disable full module graph. So we can only check the name.
+            if (shouldBeArchived && !c.getName().contains("$Proxy0")) {
+                throw new RuntimeException("Proxy class " + c + " does not seem to be archived");
+            }
+            if (!shouldBeArchived && c.getName().contains("$Proxy00")) {
+                throw new RuntimeException("Proxy class " + c + " shouldn't be archived");
+            }
+
+            System.out.println(c + (shouldBeArchived ? " is " : " is not ") + "archived, as expected");
+        }
+    }
+
+    public static void main(String args[]) {
+        // We should have archived dynamic proxies after the static archive has been dumped.
+        hasArchivedProxies = args[0].contains("PRODUCTION");
+
+        // Create a Proxy for the java.util.Map interface
+        System.out.println("================test 1: Proxy for java.util.Map");
+        Map instance1 = (Map) Proxy.newProxyInstance(
+            DynamicProxyApp.class.getClassLoader(),
+            new Class[] { Map.class },
+            new MyInvocationHandler());
+        System.out.println(instance1.getClass());
+        System.out.println(instance1.getClass().getClassLoader());
+        System.out.println(instance1.getClass().getPackage());
+        System.out.println(instance1.get("5678"));
+        System.out.println(instance1.getClass().getModule());
+
+        // Proxy$ProxyBuilder defines the proxy classes with a null protection domain, resulting in the
+        // following behavior. This must be preserved by CDS.
+        // TODO: after JDK-8322322 is implemented, add tests for Proxy creation by boot loader code.
+        ProtectionDomain pd = instance1.getClass().getProtectionDomain();
+        Asserts.assertNotNull(pd);
+        Asserts.assertNull(pd.getClassLoader());
+        Asserts.assertNull(pd.getCodeSource());
+
+
+        Module dynModule = instance1.getClass().getModule();
+        // instance1.getClass() should be in a dynamic module like jdk.proxy1, which
+        // contains all proxies that implement only public interfaces for the app loader.
+
+        // This module should be able to read the module that contains Map (java.base)
+        checkRead(true,  dynModule, Map.class.getModule());
+
+        // No proxies have been created for these modules yet, so they are't readable by dynModule yet.
+        checkRead(false, dynModule, DynamicProxyApp.class.getModule());
+        checkRead(false, dynModule, Filter.class.getModule());
+
+        checkArchivedProxy(instance1.getClass(), true);
+
+        System.out.println("=================test 2: Proxy for both Map and Runnable");
+        Map instance2 = (Map) Proxy.newProxyInstance(
+            DynamicProxyApp.class.getClassLoader(),
+            new Class[] { Map.class, Runnable.class },
+            new MyInvocationHandler());
+        System.out.println(instance2.getClass());
+        System.out.println(instance2.getClass().getPackage());
+        System.out.println(instance2.get("5678"));
+
+        checkArchivedProxy(instance2.getClass(), true);
+
+        System.out.println("=================test 3: Proxy for an interface in unnamed module");
+        DynamicProxyTest.Foo instance3 = (DynamicProxyTest.Foo) Proxy.newProxyInstance(
+            DynamicProxyApp.class.getClassLoader(),
+            new Class[] { DynamicProxyTest.Foo.class, Runnable.class },
+            new MyInvocationHandler());
+        System.out.println(instance3.getClass());
+        System.out.println(instance3.getClass().getPackage());
+        System.out.println(instance3.bar());
+        System.out.println(instance3.getClass().getModule());
+
+        // Now, dynModule should have access to the UNNAMED module
+        Asserts.assertSame(instance3.getClass().getModule(), dynModule, "proxies of only public interfaces should go in the same module");
+        checkRead(true, dynModule, DynamicProxyApp.class.getModule());
+        checkArchivedProxy(instance3.getClass(), true);
+
+        if (!args[0].equals("CLASSLIST") && !args[0].startsWith("TRAINING")) {
+            // This dynamic proxy is not loaded in the training run, so it shouldn't be
+            // archived.
+            System.out.println("=================test 3: Proxy (unarchived) for an interface in unnamed module");
+            DynamicProxyTest.Boo instance3a = (DynamicProxyTest.Boo) Proxy.newProxyInstance(
+                DynamicProxyApp.class.getClassLoader(),
+                new Class[] { DynamicProxyTest.Boo.class, Runnable.class },
+                new MyInvocationHandler());
+            System.out.println(instance3a.getClass());
+            System.out.println(instance3a.getClass().getPackage());
+            System.out.println(instance3a.far());
+
+            Asserts.assertSame(instance3.getClass().getPackage(), instance3a.getClass().getPackage(), "should be the same package");
+            checkArchivedProxy(instance3a.getClass(), false);
+        }
+
+        System.out.println("=================test 4: Proxy for java.util.logging.Filter in java.logging module");
+        Filter instance4 = (Filter) Proxy.newProxyInstance(
+            DynamicProxyApp.class.getClassLoader(),
+            new Class[] { Filter.class },
+            new MyInvocationHandler());
+        System.out.println(instance4.getClass());
+        System.out.println(instance4.getClass().getPackage());
+        System.out.println(instance4.isLoggable(null));
+
+        // Now dynModule should have access to the java.logging module
+        Asserts.assertSame(instance4.getClass().getModule(), dynModule, "proxies of only public interfaces should go in the same module");
+        checkRead(true, dynModule, Filter.class.getModule());
+
+        System.out.println("=================test 5: Proxy for non-public interface not in any package");
+        DynamicProxyTest.Coo instance5 = (DynamicProxyTest.Coo) Proxy.newProxyInstance(
+            DynamicProxyApp.class.getClassLoader(),
+            new Class[] { DynamicProxyTest.Coo.class},
+            new MyInvocationHandler());
+        System.out.println(instance5.getClass());
+        System.out.println(instance5.getClass().getPackage());
+        System.out.println(instance5.getClass().getModule());
+        System.out.println(instance5.car());
+
+        Asserts.assertSame(instance5.getClass().getPackage(), DynamicProxyApp.class.getPackage(), "should be the same package");
+        Asserts.assertSame(instance5.getClass().getModule(), DynamicProxyApp.class.getModule(), "should be the same module");
+        checkArchivedProxy(instance5.getClass(), false); // CDS doesn't (yet) support such proxies.
+
+        System.out.println("=================test 6: Proxy for non-public interface in a package");
+        {
+            Class<?> c = testpkg.PublicTester.test();
+            checkArchivedProxy(c, false); // CDS doesn't (yet) support such proxies.
+        }
+
+        System.out.println("=================test 7: Proxy with a default method that references unlinked classes");
+        DynamicProxyTest.DefaultMethodWithUnlinkedClass instance7 = (DynamicProxyTest.DefaultMethodWithUnlinkedClass) Proxy.newProxyInstance(
+            DynamicProxyApp.class.getClassLoader(),
+            new Class[] { DynamicProxyTest.DefaultMethodWithUnlinkedClass.class, Runnable.class },
+            new MyInvocationHandler());
+        System.out.println(instance7.getClass());
+        System.out.println(instance7.getClass().getPackage());
+        System.out.println(instance7.doit(false));
+        System.out.println(instance7.getClass().getModule());
+
+        Asserts.assertSame(instance7.getClass().getModule(), dynModule, "proxies of only public interfaces should go in the same module");
+        checkArchivedProxy(instance7.getClass(), true);
+
+        // Get annotations -- this will cause a bunch of proxies to be generated
+        ClassLoader loader = DynamicProxyApp.class.getClassLoader();
+        for (String className: classes) {
+            try {
+                Class.forName(className, false, loader).getDeclaredAnnotations();
+            } catch (Throwable t) {}
+        }
+    }
+}
+
+class Fruit {
+    void peel() {}
+    static Fruit get() {
+        return new Apple();
+    }
+}
+
+// This class is never included in the JAR files. This means that the Fruit class cannot
+// be verified. But our test case doesn't link the Fruit class, so it should run without
+// any error.
+class Apple extends Fruit {}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/ExcludedClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/ExcludedClasses.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/ExcludedClasses.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2023, 2024, 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
+ * @summary test the handling of classes that are excluded from the CDS dump.
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ *          /test/hotspot/jtreg/runtime/cds/appcds/leyden/test-classes
+ * @build ExcludedClasses Custy
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ *                 TestApp
+ *                 TestApp$Foo
+ *                 TestApp$Foo$Bar
+ *                 TestApp$Foo$ShouldBeExcluded
+ *                 TestApp$MyInvocationHandler
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar
+ *                 Custy
+ * @run driver ExcludedClasses LEYDEN
+ */
+
+import java.io.File;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.ProtectionDomain;
+import java.util.Map;
+
+import jdk.jfr.Event;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ExcludedClasses {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "TestApp";
+
+    public static void main(String[] args) throws Exception {
+        Tester t = new Tester();
+        t.run(args);
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);;
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar;
+        }
+
+        @Override
+        public String[] vmArgs(RunMode runMode) {
+            return new String[] {
+                "-Xlog:cds+resolve=trace",
+                //TEMP: uncomment the next line to see the TrainingData::_archived_training_data_dictionary
+                //"-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintTrainingInfo",
+            };
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                mainClass, runMode.name()
+            };
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            switch (runMode) {
+            case RunMode.TRAINING:
+            case RunMode.TRAINING0:
+            case RunMode.TRAINING1:
+            case RunMode.DUMP_STATIC:
+                out.shouldMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.Bar.f:I");
+                out.shouldNotMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
+            }
+        }
+    }
+}
+
+class TestApp {
+    static Object custInstance;
+
+    public static void main(String args[]) throws Exception {
+        // In new workflow, classes from custom loaders are passed from the preimage
+        // to the final image. See ClassPrelinker::record_unregistered_klasses().
+        custInstance = initFromCustomLoader();
+        System.out.println("Counter = " + Foo.hotSpot());
+    }
+
+    static Object initFromCustomLoader() throws Exception {
+        String path = "cust.jar";
+        URL url = new File(path).toURI().toURL();
+        URL[] urls = new URL[] {url};
+        URLClassLoader urlClassLoader =
+            new URLClassLoader("MyLoader", urls, null);
+        Class c = Class.forName("Custy", true, urlClassLoader);
+        return c.newInstance();
+    }
+
+    static class MyInvocationHandler implements InvocationHandler {
+        volatile static int cnt;
+
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            long start = System.currentTimeMillis();
+            while (System.currentTimeMillis() - start < 20) {
+                cnt += 2;
+                for (int i = 0; i < 1000; i++) {
+                    int n = cnt - 2;
+                    if (n < 2) {
+                        n = 2;
+                    }
+                    cnt += (i + cnt) % n + cnt % 2;
+                }
+            }
+            return Integer.valueOf(cnt);
+        }
+    }
+
+    static class Foo {
+        volatile static int counter;
+        static Class c = ShouldBeExcluded.class;
+
+        static Map mapProxy = (Map) Proxy.newProxyInstance(
+            Foo.class.getClassLoader(), 
+            new Class[] { Map.class },
+            new MyInvocationHandler());
+
+        static int hotSpot() {
+            ShouldBeExcluded s = new ShouldBeExcluded();
+            Bar b = new Bar();
+
+            long start = System.currentTimeMillis();
+            while (System.currentTimeMillis() - start < 1000) {
+                lambdaHotSpot();
+                s.hotSpot2();
+                b.hotSpot3();
+
+                // Currently, generated proxy classes are excluded from the CDS archive
+                Integer i = (Integer)mapProxy.get(null);
+                counter += i.intValue();
+
+
+                if (custInstance != null) {
+                    // For new workflow only:
+                    // Currently, classes loaded by custom loaders are included in the preimage run
+                    // but excluded from the final image.
+                    counter += custInstance.equals(null) ? 1 : 2;
+                }
+            }
+
+            return counter + s.m() + s.f + b.m() + b.f;
+        }
+
+        static void f() {
+            if (counter % 2 == 1) {
+                counter ++;
+            }
+        }
+
+        // Lambda classes should be excluded from new workflow training run
+        static void lambdaHotSpot() {
+            long start = System.currentTimeMillis();
+            while (System.currentTimeMillis() - start < 20) {
+                doit(() -> {
+                        counter ++;
+                    });
+            }
+        }
+
+        static void doit(Runnable r) {
+            r.run();
+        }
+
+        // All subclasses of jdk.jfr.Event are excluded from the CDS archive.
+        static class ShouldBeExcluded extends jdk.jfr.Event {
+            int f = (int)(System.currentTimeMillis()) + 123;
+            int m() {
+                return f + 456;
+            }
+
+            void hotSpot2() {
+                long start = System.currentTimeMillis();
+                while (System.currentTimeMillis() - start < 20) {
+                    for (int i = 0; i < 50000; i++) {
+                        counter += i;
+                    }
+                    f();
+                }
+            }
+        }
+
+        static class Bar {
+            int f = (int)(System.currentTimeMillis()) + 123;
+            int m() {
+                return f + 456;
+            }
+
+            void hotSpot3() {
+                long start = System.currentTimeMillis();
+                while (System.currentTimeMillis() - start < 20) {
+                    for (int i = 0; i < 50000; i++) {
+                        counter += i;
+                    }
+                    f();
+                }
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenAndOldClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenAndOldClasses.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenAndOldClasses.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2024, 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
+ * @summary CDS should fail to load if production time GC flags do not match training run.
+ * @requires vm.flagless
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @build OldClass
+ * @build LeydenAndOldClasses
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AppUsesOldClass OldClass
+ * @run driver LeydenAndOldClasses
+ */
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class LeydenAndOldClasses {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "AppUsesOldClass";
+
+    public static void main(String[] args) throws Exception {
+        Tester tester = new Tester();
+        tester.run(new String[] {"LEYDEN"} );
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {"-Xlog:cds+class=debug", mainClass};
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (runMode == RunMode.PRODUCTION) {
+                out.shouldContain("OldClass@");
+            } else if (runMode == RunMode.TRAINING || runMode == RunMode.TRAINING0 || runMode == RunMode.TRAINING1) {
+                // !!! Leyden Repo Only !!!
+                // When AOTClassLinking is enabled, we can safely archive old classes. See comments around
+                // CDSConfig::preserve_all_dumptime_verification_states().
+                out.shouldMatch("cds,class.* = 0x.* app *OldClass aot-linked");
+            }
+        }
+    }
+}
+
+class AppUsesOldClass {
+    public static void main(String args[]) {
+        System.out.println("Old Class Instance: " + new OldClass());
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenGCFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenGCFlags.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenGCFlags.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2024, 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
+ * @summary CDS should fail to load if production time GC flags do not match training run.
+ * @requires vm.flagless
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @build LeydenGCFlags
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar HelloApp
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI LeydenGCFlags
+ */
+
+import jdk.test.whitebox.WhiteBox;
+import jdk.test.whitebox.gc.GC;
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.StringArrayUtils;
+
+public class LeydenGCFlags {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "HelloApp";
+    static final String ERROR_GC_SUPPORTED = "Cannot create the CacheDataStore: UseCompressedClassPointers must be enabled, and collector must be G1, Parallel, Serial, Epsilon, or Shenandoah";
+    static final String ERROR_GC_MISMATCH = "CDS archive has aot-linked classes. It cannot be used because GC used during dump time (.*) is not the same as runtime (.*)";
+    static final String ERROR_COOP_MISMATCH = "Disable Startup Code Cache: 'HelloApp.cds.code' was created with CompressedOops::shift.. = .* vs current .*";
+
+    static String trainingArgs[];
+    static String productionArgs[];
+    static boolean shouldFailDump;
+    static boolean shouldFailRun;
+    static String productFailPattern;
+
+    public static void main(String[] args) throws Exception {
+        if (GC.Z.isSupported()) {
+            // ZGC not supported for now
+            fail_dump("-XX:+UseZGC",  "-Xmx8g",  ERROR_GC_SUPPORTED);
+        }
+
+        // Serial, Parallel and Shenandoah collectors are allowed to be used,
+        // as long as the same one is used between training and production
+        if (GC.Serial.isSupported()) {
+            good("-XX:+UseSerialGC",   "-XX:+UseSerialGC");
+        }
+        if (GC.Parallel.isSupported()) {
+            good("-XX:+UseParallelGC", "-XX:+UseParallelGC");
+        }
+        if (GC.Shenandoah.isSupported()) {
+            good("-XX:+UseShenandoahGC", "-XX:+UseShenandoahGC");
+        }
+
+        // Fail if production uses a different collector than training
+        if (GC.Parallel.isSupported() && GC.G1.isSupported()) {
+            fail_run("-XX:+UseParallelGC", "-XX:+UseG1GC",        ERROR_GC_MISMATCH );
+        }
+        if (GC.Parallel.isSupported()) {
+            fail_run(null,                 "-XX:+UseParallelGC",  ERROR_GC_MISMATCH );
+        }
+
+       if (false) { // Disabled for now, as on MacOS we cannot guarantee to get differnt coop encodings
+        // Different oop encodings
+        fail_run(array("-XX:-UseCompatibleCompressedOops", "-Xmx128m"), 
+                 array("-XX:-UseCompatibleCompressedOops","-Xmx8g"),
+                 ERROR_COOP_MISMATCH);
+        fail_run(array("-XX:-UseCompatibleCompressedOops", "-Xmx8g"),
+                 array("-XX:-UseCompatibleCompressedOops","-Xmx128m"),
+                 ERROR_COOP_MISMATCH);
+       }
+
+        // FIXME -- this causes
+        // java.lang.invoke.WrongMethodTypeException: handle's method type ()Object but found ()Object
+     /* fail_run(null,                 "NoSuchApp"); */
+    }
+
+    static String[] array(String... strings) {
+        return strings;
+    }
+    static void good(Object t, Object p) throws Exception {
+        shouldFailDump = false;
+        shouldFailRun = false;
+        run(t, p);
+    }
+
+    static void fail_dump(Object t, Object p, String regexp) throws Exception {
+        shouldFailDump = true;
+        shouldFailRun  = true;
+        productFailPattern = regexp;
+        trainingArgs = makeArgs(t);
+        productionArgs = makeArgs(p);
+        Tester tester = new Tester();
+        tester.setCheckExitValue(false);
+        tester.run(new String[] {"LEYDEN_TRAINONLY"} );
+    }
+
+    static void fail_run(Object t, Object p, String regexp) throws Exception {
+        shouldFailDump = false;
+        shouldFailRun  = true;
+        productFailPattern = regexp;
+        run(t, p);
+    }
+
+    static void run(Object t, Object p) throws Exception {
+        trainingArgs = makeArgs(t);
+        productionArgs = makeArgs(p);
+        Tester tester = new Tester();
+        tester.run(new String[] {"LEYDEN"} );
+    }
+
+    static String[] makeArgs(Object o) {
+        if (o == null) {
+            return new String[0];
+        }
+        if (o instanceof String[]) {
+            return (String[])o;
+        } else {
+            return new String[] { (String)o };
+        }
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            if (runMode.isProductionRun()) {
+                return StringArrayUtils.concat(productionArgs, "-Xshare:auto", mainClass);
+            } else {
+                return StringArrayUtils.concat(trainingArgs, mainClass);
+            }
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (shouldFailDump) {
+                if (runMode != RunMode.PRODUCTION) {
+                    out.shouldMatch(productFailPattern);
+                    out.shouldHaveExitValue(1);
+                }
+            } else if (shouldFailRun) {
+                if (runMode == RunMode.PRODUCTION) {
+                    out.shouldMatch(productFailPattern);
+                    //out.shouldHaveExitValue(1); TODO VM should enable -Xshare:on by default
+                }
+            } else {
+                if (runMode != RunMode.TRAINING1) {
+                    out.shouldContain("Hello Leyden");
+                }
+
+                if (runMode == RunMode.TRAINING || runMode == RunMode.TRAINING1) {
+                    // We should dump the heap even if the collector is not G1
+                    out.shouldContain("Shared file region (hp)");
+                }
+
+            }
+        }
+    }
+}
+
+class HelloApp {
+    public static void main(String args[]) {
+        System.out.println("Hello Leyden");
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenHello.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenHello.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenHello.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023, 2024, 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
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @build LeydenHello
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar LeydenHelloApp
+ * @run driver LeydenHello LEYDEN
+ */
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class LeydenHello {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "LeydenHelloApp";
+
+    public static void main(String[] args) throws Exception {
+        Tester t = new Tester();
+        t.run(args);
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                mainClass, runMode.name()
+            };
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (!runMode.isStaticDump()) {
+                out.shouldContain("Hello Leyden " + runMode.name());
+            }
+        }
+    }
+}
+
+class LeydenHelloApp {
+    public static void main(String args[]) {
+        System.out.println("Hello Leyden " + args[0]);
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenReflection.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenReflection.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/LeydenReflection.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2023, 2024, 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
+ * @requires vm.cds.write.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @build LeydenReflection
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar LeydenReflectionApp
+ * @run driver LeydenReflection LEYDEN
+ */
+
+import java.lang.reflect.Method;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class LeydenReflection {
+    static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+    static final String mainClass = "LeydenReflectionApp";
+
+    public static void main(String[] args) throws Exception {
+        Tester t = new Tester();
+        t.run(args);
+    }
+
+    static class Tester extends CDSAppTester {
+        public Tester() {
+            super(mainClass);
+        }
+
+        @Override
+        public String classpath(RunMode runMode) {
+            return appJar;
+        }
+
+        @Override
+        public String[] appCommandLine(RunMode runMode) {
+            return new String[] {
+                mainClass, runMode.name()
+            };
+        }
+
+        @Override
+        public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+            if (!runMode.isStaticDump()) {
+                out.shouldContain("Hello Leyden Reflection " + runMode.name());
+            }
+
+            if (runMode == RunMode.DUMP_STATIC || runMode == RunMode.TRAINING || runMode == RunMode.TRAINING1) {
+              out.shouldContain("Generate ReflectionData: LeydenReflectionApp");
+              out.shouldContain("Generate ReflectionData: java.util.Random");
+            }
+        }
+    }
+}
+
+class LeydenReflectionApp {
+    public static void main(String args[]) throws Exception {
+        // Random.<clinit> calls Random.class.getDeclaredField("seed")
+        Object x = new java.util.Random();
+
+        Method m = LeydenReflectionApp.class.getDeclaredMethod("sayHello", String.class);
+        m.invoke(null, args[0]);
+    }
+
+    static void sayHello(String s) {
+        System.out.println("Hello Leyden Reflection " + s);
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/OldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/leyden/OldClass.jasm
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/OldClass.jasm
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+super public class OldClass
+	version 50:0
+{
+  public Method "<init>":"()V"
+	stack 1 locals 1
+  {
+		aload_0;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		return;
+  }
+
+} // end Class OldClass
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/test-classes/Custy.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/test-classes/Custy.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/test-classes/Custy.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+public class Custy {
+    volatile static int cnt;
+
+    @Override
+    public boolean equals(Object other) {
+        long start = System.currentTimeMillis();
+        while (System.currentTimeMillis() - start < 20) {
+            cnt += 2;
+            for (int i = 0; i < 1000; i++) {
+                int n = cnt - 2;
+                if (n < 2) {
+                    n = 2;
+                }
+                cnt += (i + cnt) % n + cnt % 2;
+            }
+        }
+        return (cnt % 17) == 8;
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/leyden/testpkg/PublicTester.java b/test/hotspot/jtreg/runtime/cds/appcds/leyden/testpkg/PublicTester.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/leyden/testpkg/PublicTester.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ */
+
+package testpkg;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import jdk.test.lib.Asserts;
+
+public class PublicTester {
+    public static Class<?> test() {
+        NonPublicInterface npi = (NonPublicInterface) Proxy.newProxyInstance(
+            PublicTester.class.getClassLoader(), 
+            new Class[] { NonPublicInterface.class, Runnable.class },
+            new MyInvocationHandler2());
+        System.out.println(npi.getClass());
+        System.out.println(npi.getClass().getPackage());
+        System.out.println(npi.getClass().getModule());
+        System.out.println(PublicTester.class.getModule());
+        System.out.println(npi.m());
+
+        Asserts.assertSame(PublicTester.class.getModule(), npi.getClass().getModule(),
+                           "proxies for non-public interfaces should be in same module as the interface");
+        Asserts.assertSame(PublicTester.class.getPackage(), npi.getClass().getPackage(),
+                           "proxies for non-public interfaces should be in same package as the interface");
+
+        return npi.getClass();
+    }
+}
+
+class MyInvocationHandler2 implements InvocationHandler {
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        return method.getName() + " in() " + proxy.getClass() + " is called";
+    }
+}
+
+interface NonPublicInterface {
+    Object m();
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java
--- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java
@@ -85,17 +85,18 @@
             .shouldMatch(ALWAYS("klass.* ResolvedConstantsBar app => ResolvedConstantsFoo app"))
 
             // Always resolve reference when a class references a super interface
             .shouldMatch(ALWAYS("klass.* ResolvedConstantsApp app => java/lang/Runnable boot"))
 
+/** premain allows static method pre-resolution
             // Without -XX:+AOTClassLinking:
             //   java/lang/System is in the boot loader but ResolvedConstantsApp is loaded by the app loader.
             //   Even though System is in the vmClasses list, when ResolvedConstantsApp looks up
             //   "java/lang/System" in its ConstantPool, the app loader may not have resolved the System
             //   class yet (i.e., there's no initiaited class entry for System in the app loader's dictionary)
             .shouldMatch(AOTLINK_ONLY("klass.* ResolvedConstantsApp .*java/lang/System"))
-
+**/
           // Field References ---
 
             // Always resolve references to fields in the current class or super class(es)
             .shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.b:I"))
             .shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.a:I"))
@@ -122,13 +123,14 @@
 
             // Should resolve references to own non-static method (private or public)
             .shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsBar.doBar:"))
             .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.privateInstanceCall:"))
             .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.publicInstanceCall:"))
-
+/** premain allows static method pre-resolution
             // Should not resolve references to static method
             .shouldNotMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:"))
+**/
 
             // Should resolve references to method in super type
             .shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsFoo.doBar:"))
 
             // Without -XX:+AOTClassLinking App class cannot resolve references to methods in boot classes:
@@ -146,18 +148,20 @@
             ;
 
 
         // Indy References ---
         if (aotClassLinking) {
+/** premain allows Old classes to be linked
             out.shouldContain("Cannot aot-resolve Lambda proxy because OldConsumer is excluded")
                .shouldContain("Cannot aot-resolve Lambda proxy because OldProvider is excluded")
                .shouldContain("Cannot aot-resolve Lambda proxy because OldClass is excluded")
                .shouldContain("Cannot aot-resolve Lambda proxy of interface type InterfaceWithClinit")
                .shouldMatch("klasses.* app *NormalClass[$][$]Lambda/.* hidden aot-linked inited")
                .shouldNotMatch("klasses.* app *SubOfOldClass[$][$]Lambda/")
                .shouldMatch("archived indy *CP entry.*StringConcatTest .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants")
                .shouldNotMatch("archived indy *CP entry.*StringConcatTestOld .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants");
+**/
         }
     }
 
     static String ALWAYS(String s) {
         return "cds,resolve.*archived " + s;
diff a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/pkg3/Package3A.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/pkg3/Package3A.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/pkg3/Package3A.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+package pkg3;
+
+public class Package3A {}
diff a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/pkg3/Package3B.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/pkg3/Package3B.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/pkg3/Package3B.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2024, 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.
+ *
+ */
+
+package pkg3;
+
+public class Package3B {}
diff a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java
--- a/test/lib/jdk/test/lib/cds/CDSAppTester.java
+++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java
@@ -32,34 +32,47 @@
 
 /*
  * This is a base class used for testing CDS functionalities with complex applications.
  * You can define the application by overridding the vmArgs(), classpath() and appCommandLine()
  * methods. Application-specific validation checks can be implemented with checkExecution().
+ *
+ * Note: to debug the new workflow, run jtreg with -vmoption:-DCDSAppTester.split.new.workflow=true
+ * This will run the new workflow in two separate processes that you can rerun easily inside a debugger.
+ * Also, the log files are easier to read.
 */
 abstract public class CDSAppTester {
     private final String name;
     private final String classListFile;
     private final String classListFileLog;
     private final String staticArchiveFile;
     private final String staticArchiveFileLog;
     private final String dynamicArchiveFile;
     private final String dynamicArchiveFileLog;
+    private final String cdsFile;        // new workflow: -XX:CacheDataStore=<foo>.cds
+    private final String cdsFileLog;
+    private final String cdsFilePreImage;        // new workflow: -XX:CacheDataStore=<foo>.cds
+    private final String cdsFilePreImageLog;
+    private final String aotFile;        // new workflow = cdsFile + ".code"
     private int numProductionRuns = 0;
 
     public CDSAppTester(String name) {
         if (CDSTestUtils.DYNAMIC_DUMP) {
             throw new SkippedException("Tests based on CDSAppTester should be excluded when -Dtest.dynamic.cds.archive is specified");
         }
 
-        // Old workflow
         this.name = name;
         classListFile = name() + ".classlist";
         classListFileLog = classListFile + ".log";
         staticArchiveFile = name() + ".static.jsa";
         staticArchiveFileLog = staticArchiveFile + ".log";
         dynamicArchiveFile = name() + ".dynamic.jsa";
         dynamicArchiveFileLog = dynamicArchiveFile + ".log";
+        cdsFile = name() + ".cds";
+        cdsFileLog = cdsFile + ".log";
+        cdsFilePreImage = cdsFile + ".preimage";
+        cdsFilePreImageLog = cdsFilePreImage + ".log";
+        aotFile = cdsFile + ".code";
     }
 
     private String productionRunLog() {
         if (numProductionRuns == 0) {
             return name() + ".production.log";
@@ -69,16 +82,20 @@
     }
 
     private enum Workflow {
         STATIC,        // classic -Xshare:dump workflow
         DYNAMIC,       // classic -XX:ArchiveClassesAtExit
+        LEYDEN,        // The new "one step training workflow" -- see JDK-8320264
     }
 
     public enum RunMode {
         CLASSLIST,
         DUMP_STATIC,
         DUMP_DYNAMIC,
+        TRAINING,          // LEYDEN only
+        TRAINING0,         // LEYDEN only
+        TRAINING1,         // LEYDEN only
         PRODUCTION;
 
         public boolean isStaticDump() {
             return this == DUMP_STATIC;
         }
@@ -121,10 +138,14 @@
 
     public final boolean isDynamicWorkflow() {
         return workflow == Workflow.DYNAMIC;
     }
 
+    public final boolean isLeydenWorkflow() {
+        return workflow == Workflow.LEYDEN;
+    }
+
     private String logToFile(String logFile, String... logTags) {
         StringBuilder sb = new StringBuilder("-Xlog:");
         String prefix = "";
         for (String tag : logTags) {
             sb.append(prefix);
@@ -152,11 +173,11 @@
             listOutputFile(logFile);
         }
         if (checkExitValue) {
             output.shouldHaveExitValue(0);
         }
-        output.shouldNotContain(CDSTestUtils.MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE);
+        //output.shouldNotContain(CDSTestUtils.MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE); // FIXME -- leyden+JEP483 merge
         CDSTestUtils.checkCommonExecExceptions(output);
         checkExecution(output, runMode);
         return output;
     }
 
@@ -184,32 +205,94 @@
                                                    logToFile(staticArchiveFileLog,
                                                              "cds=debug",
                                                              "cds+class=debug",
                                                              "cds+heap=warning",
                                                              "cds+resolve=debug"));
+
         return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog);
     }
 
     private OutputAnalyzer dumpDynamicArchive() throws Exception {
         RunMode runMode = RunMode.DUMP_DYNAMIC;
         String[] cmdLine = new String[0];
-        if (isDynamicWorkflow()) {
-          // "classic" dynamic archive
-          cmdLine = StringArrayUtils.concat(vmArgs(runMode),
-                                            "-Xlog:cds",
-                                            "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile,
-                                            "-cp", classpath(runMode),
-                                            logToFile(dynamicArchiveFileLog,
-                                                      "cds=debug",
-                                                      "cds+class=debug",
-                                                      "cds+resolve=debug",
-                                                      "class+load=debug"));
-        }
+        // "classic" dynamic archive
+        cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                          "-Xlog:cds",
+                                          "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile,
+                                          "-cp", classpath(runMode),
+                                          logToFile(dynamicArchiveFileLog,
+                                                    "cds=debug",
+                                                    "cds+class=debug",
+                                                    "cds+resolve=debug",
+                                                    "class+load=debug"));
         cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
         return executeAndCheck(cmdLine, runMode, dynamicArchiveFile, dynamicArchiveFileLog);
     }
 
+    private String trainingLog(String file) {
+        return logToFile(file,
+                         "cds=debug",
+                         "cds+class=debug",
+                         "cds+heap=warning",
+                         "cds+resolve=debug");
+    }
+
+    // normal training workflow (main JVM process spawns child process)
+    private OutputAnalyzer trainingRun() throws Exception {
+        RunMode runMode = RunMode.TRAINING;
+        File f = new File(cdsFile);
+        f.delete();
+        String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                   "-XX:+AOTClassLinking",
+                                                   "-XX:+ArchiveDynamicProxies",
+                                                 //"-XX:+ArchiveReflectionData",
+                                                   "-XX:CacheDataStore=" + cdsFile,
+                                                   "-cp", classpath(runMode),
+                                                   // Use PID to distinguish the logs of the training process
+                                                   // and the forked final image dump process.
+                                                   "-Xlog:cds::uptime,level,tags,pid",
+                                                   trainingLog(cdsFileLog));
+        cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+        OutputAnalyzer out =  executeAndCheck(cmdLine, runMode, cdsFile, cdsFileLog);
+        listOutputFile(cdsFile + ".log.0"); // the preimage dump
+        return out;
+    }
+
+    // "split" training workflow (launch the two processes manually, for easier debugging);
+    private OutputAnalyzer trainingRun0() throws Exception {
+        RunMode runMode = RunMode.TRAINING0;
+        File f = new File(cdsFile);
+        f.delete();
+        String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                   "-XX:+UnlockDiagnosticVMOptions",
+                                                   "-XX:+CDSManualFinalImage",
+                                                   "-XX:+AOTClassLinking",
+                                                   "-XX:+ArchiveDynamicProxies",
+                                                 //"-XX:+ArchiveReflectionData",
+                                                   "-XX:CacheDataStore=" + cdsFile,
+                                                   "-cp", classpath(runMode),
+                                                   trainingLog(cdsFilePreImageLog));
+        cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+        return executeAndCheck(cmdLine, runMode, cdsFilePreImage, cdsFilePreImageLog);
+    }
+    private OutputAnalyzer trainingRun1() throws Exception {
+        RunMode runMode = RunMode.TRAINING1;
+        File f = new File(cdsFile);
+        f.delete();
+        String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                   "-XX:+UnlockDiagnosticVMOptions",
+                                                   "-XX:+AOTClassLinking",
+                                                   "-XX:+ArchiveDynamicProxies",
+                                                 //"-XX:+ArchiveReflectionData",
+                                                   "-XX:CacheDataStore=" + cdsFile,
+                                                   "-XX:CDSPreimage=" + cdsFilePreImage,
+                                                   "-cp", classpath(runMode),
+                                                   trainingLog(cdsFileLog));
+        cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+        return executeAndCheck(cmdLine, runMode, cdsFile, aotFile, cdsFileLog);
+    }
+
     private OutputAnalyzer productionRun() throws Exception {
         return productionRun(null, null);
     }
 
     public OutputAnalyzer productionRun(String[] extraVmArgs) throws Exception {
@@ -228,10 +311,16 @@
 
         if (isStaticWorkflow()) {
             cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + staticArchiveFile);
         } else if (isDynamicWorkflow()) {
             cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + dynamicArchiveFile);
+        } else {
+            cmdLine = StringArrayUtils.concat(cmdLine, "-XX:CacheDataStore=" + cdsFile);
+        }
+
+        if (extraVmArgs != null) {
+            cmdLine = StringArrayUtils.concat(cmdLine, extraVmArgs);
         }
 
         if (extraVmArgs != null) {
             cmdLine = StringArrayUtils.concat(cmdLine, extraVmArgs);
         }
@@ -260,10 +349,14 @@
         } else {
             if (args[0].equals("STATIC")) {
                 runStaticWorkflow();
             } else if (args[0].equals("DYNAMIC")) {
                 runDynamicWorkflow();
+            } else if (args[0].equals("LEYDEN")) {
+                runLeydenWorkflow(false);
+            } else if (args[0].equals("LEYDEN_TRAINONLY")) {
+                runLeydenWorkflow(true);
             } else {
                 throw new RuntimeException(err);
             }
         }
     }
@@ -278,6 +371,19 @@
     private void runDynamicWorkflow() throws Exception {
         this.workflow = Workflow.DYNAMIC;
         dumpDynamicArchive();
         productionRun();
     }
+
+    private void runLeydenWorkflow(boolean trainOnly) throws Exception {
+        this.workflow = Workflow.LEYDEN;
+        if (System.getProperty("CDSAppTester.split.new.workflow") != null) {
+            trainingRun0();
+            trainingRun1();
+        } else {
+            trainingRun();
+        }
+        if (!trainOnly) {
+            productionRun();
+        }
+    }
 }
diff a/test/lib/jdk/test/lib/cds/CDSOptions.java b/test/lib/jdk/test/lib/cds/CDSOptions.java
--- a/test/lib/jdk/test/lib/cds/CDSOptions.java
+++ b/test/lib/jdk/test/lib/cds/CDSOptions.java
@@ -30,10 +30,11 @@
     public String xShareMode = "on";
     public String archiveName;
     public ArrayList<String> prefix = new ArrayList<String>();
     public ArrayList<String> suffix = new ArrayList<String>();
     public boolean useSystemArchive = false;
+    public boolean benchmarkMode = false;
     public String appJar;
     public String appJarDir;
 
     // classes to be archived
     public String[] classList;
@@ -94,10 +95,15 @@
     public CDSOptions setUseSystemArchive(boolean use) {
         this.useSystemArchive = use;
         return this;
     }
 
+    public CDSOptions setBenchmarkMode(boolean mode) {
+        this.benchmarkMode = mode;
+        return this;
+    }
+
     public CDSOptions setClassList(String[] list) {
         this.classList = list;
         return this;
     }
     public CDSOptions setClassList(ArrayList<String> list) {
diff a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java
--- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java
+++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java
@@ -284,11 +284,11 @@
             output.shouldContain("Loading classes to share");
         } else {
             output.shouldContain("Written dynamic archive 0x");
         }
         output.shouldHaveExitValue(0);
-        output.shouldNotContain(MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE);
+        //output.shouldNotContain(MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE); // FIXME -- leyden+JEP483 merge
 
         for (String match : extraMatches) {
             output.shouldContain(match);
         }
 
@@ -297,11 +297,11 @@
 
     // check result of dumping base archive
     public static OutputAnalyzer checkBaseDump(OutputAnalyzer output) throws Exception {
         output.shouldContain("Loading classes to share");
         output.shouldHaveExitValue(0);
-        output.shouldNotContain(MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE);
+        //output.shouldNotContain(MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE); // FIXME -- leyden+JEP483 merge
         return output;
     }
 
     // A commonly used convenience methods to create an archive and check the results
     // Creates an archive and checks for errors
@@ -438,11 +438,13 @@
         if (!opts.useSystemArchive) {
             if (opts.archiveName == null)
                 opts.archiveName = getDefaultArchiveName();
             cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
         }
-        addVerifyArchivedFields(cmd);
+        if (!opts.benchmarkMode) {
+          addVerifyArchivedFields(cmd);
+        }
 
         if (opts.useVersion)
             cmd.add("-version");
 
         for (String s : opts.suffix) cmd.add(s);
